Add Sentry to MapLibreAndroidTestApp (#3716)

Co-authored-by: Bart Louwers <bart@emeel.net>
Co-authored-by: Bart Louwers <bart.louwers@gmail.com>
This commit is contained in:
Adrian Cojocaru
2025-08-14 16:49:02 +03:00
committed by GitHub
parent 213d1c6187
commit 8908056d5f
7 changed files with 288 additions and 76 deletions

View File

@@ -0,0 +1,87 @@
name: setup-android-ci
description: "Sets up Android CI environment"
runs:
using: "composite"
steps:
- name: Free Disk Space (Ubuntu)
if: startsWith(runner.name, 'GitHub Actions')
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: false
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- name: Validate VERSION
run: .github/scripts/validate-version.sh platform/android/VERSION
working-directory: .
shell: bash
- run: echo "cmake.dir=$(dirname "$(dirname "$(command -v cmake)")")" >> local.properties
shell: bash
- uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
- name: Get CMake and Ninja
uses: lukka/get-cmake@latest
with:
cmakeVersion: 3.24.1
ninjaVersion: latest
- uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
- name: npm install
run: npm install --ignore-scripts
working-directory: .
shell: bash
- name: run platform/android/scripts/generate-style-code.mjs
run: node platform/android/scripts/generate-style-code.mjs
working-directory: .
shell: bash
- run: |
python3 -m venv venv
source venv/bin/activate
pip3 install pre-commit
shell: bash
- run: |
source venv/bin/activate
pre-commit run clang-format --all-files
continue-on-error: true # this can mean files are modified, which is not an error
shell: bash
- run: |
source venv/bin/activate
pre-commit run clang-format --all-files
rm -rf venv
shell: bash
- uses: infotroph/tree-is-clean@v1
with:
check_untracked: true
- uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ github.job }}
append-timestamp: false
max-size: 5G
- name: restore-gradle-cache
uses: actions/cache@v4
env:
cache-name: gradle-v1
with:
path: ~/.gradle
key: ${{ env.cache-name }}-${{ hashFiles('platform/android/buildSrc/src/main/kotlin/maplibre.dependencies.gradle.kts') }}-${{ hashFiles('platform/android/build.gradle.kts') }}-${{ hashFiles('platform/android/local.properties') }}-${{ hashFiles('platform/android/gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
- ${{ env.cache-name }}

View File

@@ -51,86 +51,12 @@ jobs:
IS_LOCAL_DEVELOPMENT: false
MLN_ANDROID_STL: c++_static
steps:
- name: Free Disk Space (Ubuntu)
if: startsWith(runner.name, 'GitHub Actions')
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: false
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Validate VERSION
run: .github/scripts/validate-version.sh platform/android/VERSION
working-directory: .
- run: echo "cmake.dir=$(dirname "$(dirname "$(command -v cmake)")")" >> local.properties
- uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
- name: Get CMake and Ninja
uses: lukka/get-cmake@latest
with:
cmakeVersion: 3.24.1
ninjaVersion: latest
- uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
- name: npm install
run: npm install --ignore-scripts
working-directory: .
- name: run platform/android/scripts/generate-style-code.mjs
run: node platform/android/scripts/generate-style-code.mjs
working-directory: .
- run: |
python3 -m venv venv
source venv/bin/activate
pip3 install pre-commit
- run: |
source venv/bin/activate
pre-commit run clang-format --all-files
continue-on-error: true # this can mean files are modified, which is not an error
- run: |
source venv/bin/activate
pre-commit run clang-format --all-files
rm -rf venv
- uses: infotroph/tree-is-clean@v1
with:
check_untracked: true
- uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ github.job }}
append-timestamp: false
max-size: 5G
- name: restore-gradle-cache
uses: actions/cache@v4
env:
cache-name: gradle-v1
with:
path: ~/.gradle
key: ${{ env.cache-name }}-${{ hashFiles('platform/android/buildSrc/src/main/kotlin/maplibre.dependencies.gradle.kts') }}-${{ hashFiles('platform/android/build.gradle.kts') }}-${{ hashFiles('platform/android/local.properties') }}-${{ hashFiles('platform/android/gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
- ${{ env.cache-name }}
- uses: ./.github/actions/setup-android-ci
- name: Check code style
if: matrix.renderer == 'opengl'

View File

@@ -0,0 +1,61 @@
name: android-testapp-release
permissions:
contents: write
on:
workflow_dispatch:
jobs:
create-android-testapp-release:
runs-on: ubuntu-latest
steps:
- name: Create Release
run: |
gh release create android-testapp-${{ github.sha }} \
--draft=false \
--prerelease=false \
--latest=false \
--title android-testapp-${{ github.sha }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
android-build-testapp:
needs: [create-android-testapp-release]
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
renderer: [opengl, vulkan]
defaults:
run:
working-directory: platform/android
env:
BUILDTYPE: Debug
IS_LOCAL_DEVELOPMENT: false
MLN_ANDROID_STL: c++_static
SENTRY_ORG: maplibre
SENTRY_PROJECT: maplibre-android
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN_ANDROID }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- uses: ./.github/actions/setup-android-ci
- run: ./gradlew assemble${{ matrix.renderer }}Release
- name: Install sentry-cli
run: curl -sL https://sentry.io/get-cli/ | sh
- name: Upload debug symbols to sentry
run: sentry-cli debug-files upload MapLibreAndroidTestApp
- name: Upload Linux artifact
run: |
gh release upload android-testapp-${{ github.sha }} MapLibreAndroidTestApp/build/outputs/apk/${{ matrix.renderer }}/release/MapLibreAndroidTestApp-${{ matrix.renderer }}-release.apk
env:
GH_TOKEN: ${{ github.token }}

View File

@@ -9,7 +9,6 @@ plugins {
id("maplibre.gradle-lint")
}
fun obtainTestBuildType(): String {
return if (project.hasProperty("testBuildType")) {
project.properties["testBuildType"] as String
@@ -31,6 +30,7 @@ android {
versionName = "6.0.1"
testInstrumentationRunner = "org.maplibre.android.InstrumentationRunner"
multiDexEnabled = true
manifestPlaceholders["SENTRY_DSN"] = ""
}
nativeBuild(listOf("example-custom-layer"))
@@ -47,6 +47,14 @@ android {
isMinifyEnabled = false
isShrinkResources = false
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
packaging {
jniLibs {
keepDebugSymbols += "**/*.so"
}
}
buildConfigField("String", "SENTRY_DSN", "\"" + (System.getenv("SENTRY_DSN") ?: "") + "\"")
manifestPlaceholders["SENTRY_DSN"] = System.getenv("SENTRY_DSN") ?: ""
}
getByName("release") {
isMinifyEnabled = true
@@ -54,6 +62,9 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
testProguardFiles("test-proguard-rules.pro")
signingConfig = signingConfigs.getByName("debug")
buildConfigField("String", "SENTRY_DSN", "\"" + (System.getenv("SENTRY_DSN") ?: "") + "\"")
manifestPlaceholders["SENTRY_DSN"] = System.getenv("SENTRY_DSN") ?: ""
}
}
@@ -119,3 +130,5 @@ dependencies {
androidTestImplementation(libs.androidxTestCoreKtx)
androidTestImplementation(libs.kotlinxCoroutinesTest)
}
apply<SentryConditionalPlugin>()

View File

@@ -1283,6 +1283,68 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
</activity>
<!-- Sentry setup -->
<!-- Required: set your sentry.io project identifier (DSN) -->
<meta-data
android:name="io.sentry.dsn"
android:value="${SENTRY_DSN}"
/>
<!-- Add data like request headers, user ip address and device name, see https://docs.sentry.io/platforms/android/data-management/data-collected/ for more info -->
<meta-data
android:name="io.sentry.send-default-pii"
android:value="true"
/>
<!-- enable automatic breadcrumbs for user interactions (clicks, swipes, scrolls) -->
<meta-data
android:name="io.sentry.traces.user-interaction.enable"
android:value="true"
/>
<!-- enable screenshot for crashes -->
<meta-data
android:name="io.sentry.attach-screenshot"
android:value="true"
/>
<!-- enable view hierarchy for crashes -->
<meta-data
android:name="io.sentry.attach-view-hierarchy"
android:value="true"
/>
<!-- enable the performance API by setting a sample-rate, adjust in production env -->
<meta-data
android:name="io.sentry.traces.sample-rate"
android:value="1.0"
/>
<!-- Enable UI profiling, adjust in production env. This is evaluated only once per session -->
<meta-data
android:name="io.sentry.traces.profiling.session-sample-rate"
android:value="1.0"
/>
<!-- Set profiling mode. For more info see https://docs.sentry.io/platforms/android/profiling/#enabling-ui-profiling -->
<meta-data
android:name="io.sentry.traces.profiling.lifecycle"
android:value="trace"
/>
<!-- Enable profiling on app start. The app start profile will be stopped automatically when the app start root span finishes -->
<meta-data
android:name="io.sentry.traces.profiling.start-on-app-start"
android:value="true"
/>
<!-- record session replays for 100% of errors and 10% of sessions -->
<meta-data
android:name="io.sentry.session-replay.on-error-sample-rate"
android:value="1.0"
/>
<meta-data
android:name="io.sentry.session-replay.session-sample-rate"
android:value="0.1"
/>
</application>
</manifest>

View File

@@ -14,4 +14,5 @@ dependencies {
implementation("com.android.tools.build:gradle:8.6.0")
implementation("com.android.application:com.android.application.gradle.plugin:8.6.0")
implementation("commons-collections:commons-collections:3.2.2")
implementation("io.sentry:sentry-android-gradle-plugin:5.9.0")
}

View File

@@ -0,0 +1,62 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
class SentryConditionalPlugin : Plugin<Project> {
override fun apply(project: Project) {
val sentryProject = System.getenv("SENTRY_PROJECT")
if (sentryProject.isNullOrBlank()) {
println("SENTRY_PROJECT environment variable not set. Skipping Sentry plugin application.")
return
}
// Apply the Sentry plugin via plugin id
val sentryPluginId = "io.sentry.android.gradle"
try {
project.pluginManager.apply(sentryPluginId)
} catch (t: Throwable) {
project.logger.warn("Could not apply Sentry plugin ($sentryPluginId): ${t.message}")
return
}
// Configure Sentry extension using reflection to avoid compile-time dependency
val sentryExt = project.extensions.findByName("sentry")
if (sentryExt == null) {
project.logger.warn("Sentry extension not found after applying the plugin. Skipping configuration.")
return
}
fun setProperty(ext: Any, propertyGetterName: String, value: Any?) {
try {
val getter = ext.javaClass.methods.firstOrNull { it.name == propertyGetterName && it.parameterCount == 0 }
?: return
val propObj = getter.invoke(ext) ?: return
val setMethod = propObj.javaClass.methods.firstOrNull { it.name == "set" && it.parameterCount == 1 }
?: return
setMethod.invoke(propObj, value)
} catch (_: Throwable) {
// Ignore if property isn't present on current plugin version
}
}
val propertiesToSet = mapOf(
"getDebug" to true,
"getOrg" to System.getenv("SENTRY_ORG"),
"getProjectName" to System.getenv("SENTRY_PROJECT"),
"getAuthToken" to System.getenv("SENTRY_AUTH_TOKEN"),
"getIncludeProguardMapping" to true,
"getAutoUploadProguardMapping" to true,
"getDexguardEnabled" to false,
"getUploadNativeSymbols" to true,
"getAutoUploadNativeSymbols" to false,
"getIncludeNativeSources" to true,
"getIncludeSourceContext" to true,
"getIncludeDependenciesReport" to true,
"getTelemetry" to false
)
propertiesToSet.forEach { (getterName, value) ->
setProperty(sentryExt, getterName, value)
}
}
}