Skip to content
Draft
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
4 changes: 2 additions & 2 deletions .github/workflows/integration-tests-macrobenchmark.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'Integration Tests - Macrobenchmark'
# Runs the sentry-uitest-android-macrobenchmark cold-start benchmark on a Sauce Labs real
# Runs the sentry-samples-android macrobenchmark cold-start benchmark on a Sauce Labs real
# device and recovers timeToInitialDisplay from the device log.
#
on:
Expand Down Expand Up @@ -33,7 +33,7 @@ jobs:

- name: Assemble target app and Macrobenchmark apk
if: env.SAUCE_USERNAME != null
run: ./gradlew :sentry-samples:sentry-samples-android:assembleRelease :sentry-android-integration-tests:sentry-uitest-android-macrobenchmark:assembleBenchmark
run: ./gradlew -p sentry-samples/sentry-samples-android assembleRelease :macrobenchmark:assembleBenchmark

- name: Run Macrobenchmark in SauceLab
uses: saucelabs/saucectl-run-action@283660aa934c02723c497efa151d582a3acc5801 # pin@v3
Expand Down
2 changes: 1 addition & 1 deletion .sauce/sentry-uitest-android-macrobenchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defaults:

espresso:
app: ./sentry-samples/sentry-samples-android/build/outputs/apk/release/sentry-samples-android-release.apk
testApp: ./sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build/outputs/apk/benchmark/sentry-uitest-android-macrobenchmark-benchmark.apk
testApp: ./sentry-samples/sentry-samples-android/macrobenchmark/build/outputs/apk/benchmark/macrobenchmark-benchmark.apk

suites:

Expand Down
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ resolves the per-module test task and the unit-test vs system-test split for you
# Assemble Android test APKs
./gradlew :sentry-android-integration-tests:sentry-uitest-android:assembleRelease :sentry-android-integration-tests:sentry-uitest-android:assembleAndroidTest

# Build the Android sample app, which is a separate Gradle build that includes this one
./gradlew -p sentry-samples/sentry-samples-android assembleDebug

# Run critical UI tests
./scripts/test-ui-critical.sh
```
Expand Down Expand Up @@ -111,7 +114,7 @@ The repository is organized into multiple modules:
### Utility Modules
- **`sentry-test-support`** - Shared test utilities
- **`sentry-system-test-support`** - System testing infrastructure
- **`sentry-samples`** - Example applications
- **`sentry-samples`** - Example applications. `sentry-samples-android` is a separate Gradle build that includes this one, so root tasks other than `spotlessApply` do not reach it
- **`sentry-bom`** - Bill of Materials for dependency management

### Key Architectural Patterns
Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleUiTestRelease assembleUiTestCriticalRelease runUiTestCritical setupPython systemTest systemTestInteractive check preMerge publish
.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleUiTestRelease assembleUiTestCriticalRelease runUiTestCritical setupPython systemTest systemTestInteractive check checkSampleAndroid preMerge publish

all: stop clean javadocs compile
assembleUiTests: assembleUiTestRelease
preMerge: check
preMerge: check checkSampleAndroid
publish: clean dryRelease

# deep clean
Expand Down Expand Up @@ -63,3 +63,7 @@ systemTestInteractive: setupPython
# Run tests and lint
check:
./gradlew check

# The Android sample is a separate build, so the root `check` does not reach it
checkSampleAndroid:
./gradlew -p sentry-samples/sentry-samples-android assembleDebug lintDebug
10 changes: 7 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,14 @@ plugins {
alias(libs.plugins.spring.dependency.management) apply false
id("io.sentry.javadoc.aggregate")
alias(libs.plugins.sentry) apply false
alias(libs.plugins.android.application) apply false
}

buildscript {
repositories {
google()
}
dependencies {
classpath(Config.BuildPlugins.androidGradle)

// add classpath of sentry android gradle plugin
// classpath("io.sentry:sentry-android-gradle-plugin:{version}")

Expand All @@ -51,7 +50,6 @@ apiValidation {
)
ignoredProjects.addAll(
listOf(
"sentry-samples-android",
"sentry-samples-console",
"sentry-samples-console-opentelemetry-noagent",
"sentry-samples-jul",
Expand Down Expand Up @@ -107,6 +105,12 @@ allprojects {
}
}

// `subprojects` below cannot reach the Android sample, which is a separate build, so delegate to it
// and keep a single `./gradlew spotlessApply` formatting the whole repository.
tasks.register("spotlessApply") {
dependsOn(gradle.includedBuild("sentry-samples-android").task(":spotlessApply"))
}

subprojects {
apply { plugin("io.sentry.spotless") }

Expand Down
5 changes: 0 additions & 5 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@

object Config {
val AGP = System.getenv("VERSION_AGP") ?: "9.2.1"
val kotlinStdLib = "stdlib-jdk8"
val kotlinStdLibVersionAndroid = "1.9.24"
val kotlinTestJunit = "test-junit"

object BuildPlugins {
val androidGradle = "com.android.tools.build:gradle:$AGP"
}

object Android {
val abiFilters = listOf("x86", "armeabi-v7a", "x86_64", "arm64-v8a")

Expand Down
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
[versions]
# Also honoured via the VERSION_AGP env var, set by the AGP compatibility matrix job.
agp = "9.2.1"
animalsniffer = "2.0.1"
apollo = "2.5.9"
androidxLifecycle = "2.2.0"
Expand Down Expand Up @@ -52,6 +54,7 @@ compileSdk = "37"
minSdk = "21"

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
Expand Down
1 change: 1 addition & 0 deletions sentry-samples/sentry-samples-android/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/build
/local.properties
14 changes: 10 additions & 4 deletions sentry-samples/sentry-samples-android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@
Sample application demonstrating how to use the Sentry Android SDK, including core functionality (error reporting, tracing, session replay,
profiling) and integrations (Compose, OkHttp, SQLite, etc.).

This directory is its own Gradle build. It includes the SDK build (`includeBuild("../..")`), so it
depends on plain `io.sentry:*` coordinates exactly like a real app would, and Gradle substitutes
them with the SDK's projects โ€” nothing needs to be published first. Every command below therefore
runs against this build, not the repository root's.

## How to run it?

Install the app on your device or emulator:

```
./gradlew :sentry-samples:sentry-samples-android:installDebug
./gradlew -p sentry-samples/sentry-samples-android installDebug
```

or simply open the project in Android Studio and run the `sentry-samples-android` configuration.
or open `sentry-samples/sentry-samples-android` in Android Studio (not the repository root) and run
the `sentry-samples-android` configuration. The SDK sources come along through the included build.

You can also apply the [Sentry Android Gradle Plugin](https://github.com/getsentry/sentry-android-gradle-plugin) (SAGP) when building (not applied by default):

```
./gradlew :sentry-samples:sentry-samples-android:installDebug -PuseSagp
./gradlew -p sentry-samples/sentry-samples-android installDebug -PuseSagp
```

In Android Studio, add `useSagp=` (empty value) to `gradle.properties`, or pass `-PuseSagp` as a Gradle project property.
Expand Down Expand Up @@ -52,7 +58,7 @@ Re-run `publishToMavenLocal` after each SAGP change.
Then build from sentry-java:

```
./gradlew :sentry-samples:sentry-samples-android:installDebug -PuseSagp
./gradlew -p sentry-samples/sentry-samples-android installDebug -PuseSagp
```

## Viewing SDK output
Expand Down
62 changes: 41 additions & 21 deletions sentry-samples/sentry-samples-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,32 @@ import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.internal.extensions.stdlib.capitalized

plugins {
id("com.android.application")
alias(libs.plugins.android.application)
id("io.sentry.spotless")
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.ksp)
alias(libs.plugins.sentry) apply false
alias(libs.plugins.sqldelight)
}

// The SDK version lives in the SDK build's gradle.properties, which this build cannot read as a
// Gradle property. It doubles as the sample's versionName, so an APK says which SDK it was built
// against.
val sentryVersion: String =
providers
.fileContents(layout.projectDirectory.dir("../..").file("gradle.properties"))
.asText
.map { properties ->
val match = Regex("""^versionName=(.+)$""", RegexOption.MULTILINE).find(properties)
checkNotNull(match) { "versionName is missing from the SDK build's gradle.properties" }
.groupValues[1]
.trim()
}
.get()

version = sentryVersion

if (providers.gradleProperty("useSagp").isPresent) {
apply(plugin = "io.sentry.android.gradle")
}
Expand Down Expand Up @@ -63,7 +81,7 @@ android {
}
}

ndk { abiFilters.addAll(Config.Android.abiFilters) }
ndk { abiFilters.addAll(listOf("x86", "armeabi-v7a", "x86_64", "arm64-v8a")) }
}

lint {
Expand Down Expand Up @@ -102,7 +120,7 @@ android {

signingConfigs {
getByName("debug") {
storeFile = rootProject.file("debug.keystore")
storeFile = rootProject.file("../../debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
Expand Down Expand Up @@ -152,7 +170,7 @@ android {
val toggleNativeLoggingTask =
project.tasks.register<ToggleNativeLoggingTask>(taskName) {
mergedManifest.set(variant.artifacts.get(SingleArtifact.MERGED_MANIFEST))
rootDir.set(project.rootDir.absolutePath)
repoDir.set(project.rootDir.resolve("../..").canonicalPath)
}
project.afterEvaluate {
(variant as? VariantImpl<*>)?.taskContainer?.assembleTask?.configure {
Expand All @@ -167,6 +185,10 @@ android {
@Suppress("UnstableApiUsage") packagingOptions { jniLibs { useLegacyPackaging = true } }
}

// The SDK build drives formatting repository-wide and references one task per included build, so
// this build's root spotlessApply has to cover its subprojects too.
tasks.named("spotlessApply") { dependsOn(subprojects.map { "${it.path}:spotlessApply" }) }

sqldelight {
databases {
create("SampleSQLDelightDatabase") {
Expand All @@ -179,22 +201,20 @@ sqldelight {
}

dependencies {
implementation(
kotlin(Config.kotlinStdLib, org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)
)

implementation(projects.sentryAndroid)
implementation(projects.sentryAndroidFragment)
implementation(projects.sentryAndroidNavigation)
implementation(projects.sentryAndroidSqlite)
implementation(projects.sentryAndroidTimber)
implementation(projects.sentryCompose)
implementation(projects.sentryKotlinExtensions)
implementation(projects.sentryOkhttp)
implementation(projects.sentrySpotlight)
implementation(kotlin("stdlib-jdk8"))

implementation("io.sentry:sentry-android:$sentryVersion")
implementation("io.sentry:sentry-android-fragment:$sentryVersion")
implementation("io.sentry:sentry-android-navigation:$sentryVersion")
implementation("io.sentry:sentry-android-sqlite:$sentryVersion")
implementation("io.sentry:sentry-android-timber:$sentryVersion")
implementation("io.sentry:sentry-compose:$sentryVersion")
implementation("io.sentry:sentry-kotlin-extensions:$sentryVersion")
implementation("io.sentry:sentry-okhttp:$sentryVersion")
implementation("io.sentry:sentry-spotlight:$sentryVersion")

// how to exclude androidx if release health feature is disabled
// implementation(projects.sentryAndroid) {
// implementation("io.sentry:sentry-android:$sentryVersion") {
// exclude(group = "androidx.lifecycle", module = "lifecycle-process")
// exclude(group = "androidx.lifecycle", module = "lifecycle-common-java8")
// exclude(group = "androidx.core", module = "core")
Expand Down Expand Up @@ -232,12 +252,12 @@ dependencies {
ksp(libs.androidx.room.compiler)
ksp(libs.androidx.room3.compiler)

debugImplementation(projects.sentryAndroidDistribution)
debugImplementation("io.sentry:sentry-android-distribution:$sentryVersion")
debugImplementation(libs.leakcanary)
}

abstract class ToggleNativeLoggingTask : Exec() {
@get:Input abstract val rootDir: Property<String>
@get:Input abstract val repoDir: Property<String>

@get:InputFile abstract val mergedManifest: RegularFileProperty

Expand All @@ -255,7 +275,7 @@ abstract class ToggleNativeLoggingTask : Exec() {
args.add(0, "cmd")
args.add(1, "/c")
}
args.add("${rootDir.get()}/scripts/toggle-codec-logs.sh")
args.add("${repoDir.get()}/scripts/toggle-codec-logs.sh")
args.add(if (value) "enable" else "disable")
commandLine(args)
super.exec()
Expand Down
38 changes: 38 additions & 0 deletions sentry-samples/sentry-samples-android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Gradle properties are per-build, so this repeats the flags the sample used to inherit from the
# repository root while it was a subproject of the SDK build. The SDK version is not repeated here;
# it is read from the root build's gradle.properties in build.gradle.kts.

# Daemons heap size
org.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=2g -XX:+CrashOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true

# AndroidX required by AGP >= 3.6.x
android.useAndroidX=true
# AGP 9+ migration opt-outs until we remove kotlin-android plugin and adopt built-in Kotlin.
android.builtInKotlin=false
android.newDsl=false
android.experimental.lint.version=9.2.1

# Override the SDK name on native crashes on Android
sentryAndroidSdkName=sentry.native.android

# disable renderscript, it's enabled by default
android.defaults.buildfeatures.renderscript=false

# disable shader compilation, it's enabled by default
android.defaults.buildfeatures.shaders=false

# disable aidl files, it's enabled by default
android.defaults.buildfeatures.aidl=false

# disable Resource Values generation
android.defaults.buildfeatures.resvalues=false

# disable automatically adding Kotlin stdlib to compile dependencies
kotlin.stdlib.default.dependency=false

android.nonTransitiveRClass=true
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# sentry-uitest-android-macrobenchmark
# macrobenchmark

Jetpack Macrobenchmark for cold-start of `sentry-samples-android`, used to evaluate SDK-init
performance changes on a real device in a **stable, reproducible** way. Not run in CI.
Expand Down Expand Up @@ -28,7 +28,7 @@ unlocked-clock device after ~14 iterations, inflating the tail of longer runs.
Connect a device, then:

```bash
./gradlew :sentry-android-integration-tests:sentry-uitest-android-macrobenchmark:connectedBenchmarkAndroidTest
./gradlew -p sentry-samples/sentry-samples-android :macrobenchmark:connectedBenchmarkAndroidTest
```

Results print to the console and are written to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11

plugins {
id("com.android.test")
id("io.sentry.spotless")
alias(libs.plugins.kotlin.android)
}

Expand Down Expand Up @@ -34,7 +35,8 @@ android {

kotlin { compilerOptions.jvmTarget = JVM_11 }

targetProjectPath = ":sentry-samples:sentry-samples-android"
// The sample app is the root project of this build.
targetProjectPath = ":"
// Run the test in its own process so it measures the target app cold, not itself.
experimentalProperties["android.experimental.self-instrumenting"] = true
}
Expand Down
Loading
Loading