mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
chore(ci): Refactor and optimize GitHub Actions workflows (#4252)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
d9bc79b396
commit
cf48d6c1c1
6 changed files with 144 additions and 59 deletions
21
.github/workflows/merge-queue.yml
vendored
21
.github/workflows/merge-queue.yml
vendored
|
|
@ -9,7 +9,14 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_and_detekt:
|
||||
lint:
|
||||
if: github.repository == 'meshtastic/Meshtastic-Android'
|
||||
uses: ./.github/workflows/reusable-lint.yml
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
|
||||
build:
|
||||
needs: lint
|
||||
if: github.repository == 'meshtastic/Meshtastic-Android'
|
||||
uses: ./.github/workflows/reusable-android-build.yml
|
||||
with:
|
||||
|
|
@ -21,10 +28,12 @@ jobs:
|
|||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
androidTest:
|
||||
needs: lint
|
||||
if: github.repository == 'meshtastic/Meshtastic-Android'
|
||||
uses: ./.github/workflows/reusable-android-test.yml
|
||||
with:
|
||||
api_levels: '[26, 35]' # Run on both API 26 and 35 for merge queue
|
||||
test_flavors: 'both' # Run both flavors for merge queue (comprehensive)
|
||||
upload_artifacts: false
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
|
|
@ -34,10 +43,9 @@ jobs:
|
|||
name: Check Workflow Status # Matches another in pull-request, and is required for merge to main.
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
[
|
||||
build_and_detekt,
|
||||
androidTest
|
||||
]
|
||||
- lint
|
||||
- build
|
||||
- androidTest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check Workflow Status
|
||||
|
|
@ -48,5 +56,6 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
}
|
||||
exit_on_result "build_and_detekt" "${{ needs.build_and_detekt.result }}"
|
||||
exit_on_result "lint" "${{ needs.lint.result }}"
|
||||
exit_on_result "build" "${{ needs.build.result }}"
|
||||
exit_on_result "androidTest" "${{ needs.androidTest.result }}"
|
||||
|
|
|
|||
85
.github/workflows/pull-request.yml
vendored
85
.github/workflows/pull-request.yml
vendored
|
|
@ -11,40 +11,95 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
build_and_detekt:
|
||||
check-changes:
|
||||
if: github.repository == 'meshtastic/Meshtastic-Android' && !( github.head_ref == 'scheduled-updates' || github.head_ref == 'l10n_main' )
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
code_changed: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
code:
|
||||
- '**/*.kt'
|
||||
- '**/*.java'
|
||||
- '**/*.xml'
|
||||
- '**/*.kts'
|
||||
- '**/*.properties'
|
||||
- 'gradle/**'
|
||||
- 'gradlew'
|
||||
- 'gradlew.bat'
|
||||
- '**/src/**'
|
||||
- '.github/workflows/**'
|
||||
|
||||
lint:
|
||||
needs: check-changes
|
||||
if: needs.check-changes.outputs.code_changed == 'true'
|
||||
uses: ./.github/workflows/reusable-lint.yml
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
|
||||
build:
|
||||
needs: lint
|
||||
if: ${{ !cancelled() && !failure() }}
|
||||
uses: ./.github/workflows/reusable-android-build.yml
|
||||
secrets: inherit
|
||||
|
||||
androidTest:
|
||||
# Assuming androidTest should also only run for the main repository
|
||||
if: github.repository == 'meshtastic/Meshtastic-Android' && !( github.head_ref == 'scheduled-updates' || github.head_ref == 'l10n_main' )
|
||||
needs: lint
|
||||
if: ${{ !cancelled() && !failure() }}
|
||||
uses: ./.github/workflows/reusable-android-test.yml
|
||||
with:
|
||||
api_levels: '[35]' # Run only on API 35 for PRs
|
||||
# upload_artifacts defaults to true, so no need to explicitly set
|
||||
test_flavors: 'google' # Run only Google flavor for PRs (faster)
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
|
||||
# This job handles the case when no code changes are detected (docs-only PRs)
|
||||
skip-notice:
|
||||
needs: check-changes
|
||||
if: needs.check-changes.outputs.code_changed != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Skip CI for non-code changes
|
||||
run: echo "Skipping CI - no code changes detected (docs/config only)"
|
||||
|
||||
check-workflow-status:
|
||||
name: Check Workflow Status
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
[
|
||||
build_and_detekt,
|
||||
androidTest
|
||||
]
|
||||
- check-changes
|
||||
- lint
|
||||
- build
|
||||
- androidTest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check Workflow Status
|
||||
run: |
|
||||
exit_on_result() {
|
||||
if [[ "$2" == "failure" || "$2" == "cancelled" ]]; then
|
||||
echo "Job '$1' failed or was cancelled."
|
||||
# If no code changed, all jobs are expected to be skipped - that's success
|
||||
if [[ "${{ needs.check-changes.outputs.code_changed }}" != "true" ]]; then
|
||||
echo "No code changes - CI jobs skipped as expected"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Code changed - check that all jobs succeeded
|
||||
check_result() {
|
||||
local job_name=$1
|
||||
local result=$2
|
||||
if [[ "$result" == "failure" ]]; then
|
||||
echo "::error::Job '$job_name' failed"
|
||||
exit 1
|
||||
elif [[ "$result" == "cancelled" ]]; then
|
||||
echo "::error::Job '$job_name' was cancelled"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
exit_on_result "build_and_detekt" "${{ needs.build_and_detekt.result }}"
|
||||
exit_on_result "androidTest" "${{ needs.androidTest.result }}"
|
||||
|
||||
check_result "lint" "${{ needs.lint.result }}"
|
||||
check_result "build" "${{ needs.build.result }}"
|
||||
check_result "androidTest" "${{ needs.androidTest.result }}"
|
||||
|
||||
echo "All jobs passed successfully"
|
||||
|
|
|
|||
8
.github/workflows/reusable-android-build.yml
vendored
8
.github/workflows/reusable-android-build.yml
vendored
|
|
@ -1,4 +1,4 @@
|
|||
name: Reusable Android Build and Detekt
|
||||
name: Reusable Android Build and Test
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
|
@ -19,7 +19,7 @@ on:
|
|||
default: true
|
||||
|
||||
jobs:
|
||||
build_and_detekt:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
|
|
@ -68,8 +68,8 @@ jobs:
|
|||
run: |
|
||||
echo "datadogApplicationId=$DATADOG_APPLICATION_ID" >> ./secrets.properties
|
||||
echo "datadogClientToken=$DATADOG_CLIENT_TOKEN" >> ./secrets.properties
|
||||
- name: Run Spotless, Detekt, Build, Lint, and Local Tests
|
||||
run: ./gradlew spotlessCheck detekt assembleDebug runAllDebugTests koverXmlReport --configuration-cache --scan
|
||||
- name: Build and Run Unit Tests
|
||||
run: ./gradlew assembleDebug testGoogleDebugUnitTest testFdroidDebugUnitTest koverXmlReport --continue --scan
|
||||
env:
|
||||
VERSION_CODE: ${{ env.VERSION_CODE }}
|
||||
|
||||
|
|
|
|||
20
.github/workflows/reusable-android-test.yml
vendored
20
.github/workflows/reusable-android-test.yml
vendored
|
|
@ -13,6 +13,11 @@ on:
|
|||
required: false
|
||||
type: string
|
||||
default: '[26, 35]' # Default to running both if not specified by caller
|
||||
test_flavors:
|
||||
description: 'Which flavors to test: "google", "fdroid", or "both"'
|
||||
required: false
|
||||
type: string
|
||||
default: 'both'
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY:
|
||||
required: false
|
||||
|
|
@ -31,7 +36,7 @@ jobs:
|
|||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: 0
|
||||
fetch-depth: 1 # Shallow clone - no version code calculation needed
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
|
|
@ -76,6 +81,17 @@ jobs:
|
|||
disable-animations: false
|
||||
script: echo "Generated AVD snapshot for caching."
|
||||
|
||||
- name: Determine test tasks
|
||||
id: test-tasks
|
||||
run: |
|
||||
if [ "${{ inputs.test_flavors }}" = "google" ]; then
|
||||
echo "tasks=connectedGoogleDebugAndroidTest" >> $GITHUB_OUTPUT
|
||||
elif [ "${{ inputs.test_flavors }}" = "fdroid" ]; then
|
||||
echo "tasks=connectedFdroidDebugAndroidTest" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tasks=connectedFdroidDebugAndroidTest connectedGoogleDebugAndroidTest" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Run Android Instrumented Tests and Generate Coverage
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
env:
|
||||
|
|
@ -86,7 +102,7 @@ jobs:
|
|||
force-avd-creation: false
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: true
|
||||
script: ./gradlew runAllConnectedDebugTests koverXmlReport --configuration-cache --scan && ( killall -INT crashpad_handler || true )
|
||||
script: ./gradlew ${{ steps.test-tasks.outputs.tasks }} koverXmlReport --continue --scan && ( killall -INT crashpad_handler || true )
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: ${{ !cancelled() }}
|
||||
|
|
|
|||
37
.github/workflows/reusable-lint.yml
vendored
Normal file
37
.github/workflows/reusable-lint.yml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
name: Reusable Lint and Format Check
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest # Lint is fast, doesn't need large runner
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'jetbrains'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
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
|
||||
|
||||
- name: Run Spotless and Detekt
|
||||
run: ./gradlew spotlessCheck detekt --scan
|
||||
|
|
@ -47,36 +47,4 @@ plugins {
|
|||
|
||||
dependencies {
|
||||
dokkaPlugin(libs.dokka.android.documentation.plugin)
|
||||
}
|
||||
|
||||
val debugTests = listOf(
|
||||
"testDebugUnitTest",
|
||||
"testFdroidDebugUnitTest",
|
||||
"testGoogleDebugUnitTest"
|
||||
)
|
||||
|
||||
tasks.register("runAllDebugTests") {
|
||||
group = "verification"
|
||||
description = "Runs all unit tests for debug variants and flavors"
|
||||
dependsOn(subprojects.map { subproject ->
|
||||
subproject.tasks.matching { task ->
|
||||
task.name in debugTests
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val connectedTests = listOf(
|
||||
"connectedDebugAndroidTest",
|
||||
"connectedFdroidDebugAndroidTest",
|
||||
"connectedGoogleDebugAndroidTest"
|
||||
)
|
||||
|
||||
tasks.register("runAllConnectedDebugTests") {
|
||||
group = "verification"
|
||||
description = "Runs all connected tests for debug variants and flavors"
|
||||
dependsOn(subprojects.map { subproject ->
|
||||
subproject.tasks.matching { task ->
|
||||
task.name in connectedTests
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue