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: 3 additions & 1 deletion .github/workflows/format-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ jobs:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}

- name: Format with spotlessApply
run: ./gradlew spotlessApply
run: |
./gradlew spotlessApply
./gradlew -p sentry-samples/sentry-samples-android spotlessApply

# actions/checkout fetches only a single commit in a detached HEAD state. Therefore
# we need to pass the current branch, otherwise we can't commit the changes.
Expand Down
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 @@ -64,6 +64,9 @@ resolves the per-module test task, including integration test suites, for you.

# Run critical UI tests
./scripts/test-ui-critical.sh

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

## Development Workflow Rules
Expand Down Expand Up @@ -110,7 +113,7 @@ The repository is organized into multiple modules:

### Utility Modules
- **`sentry-test-support`** - Shared test utilities, including `MockSentryServer` and envelope matchers
- **`sentry-samples`** - Example applications
- **`sentry-samples`** - Example applications. `sentry-samples-android` is a separate Gradle build that includes this one, so root tasks such as `check` and `spotlessApply` don't reach it
- **`sentry-bom`** - Bill of Materials for dependency management

### Key Architectural Patterns
Expand Down
9 changes: 7 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 check preMerge publish
.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleUiTestRelease assembleUiTestCriticalRelease runUiTestCritical check checkSampleAndroid preMerge publish

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

# deep clean
Expand All @@ -28,6 +28,7 @@ update:
# Spotless check's code
checkFormat:
./gradlew spotlessJavaCheck spotlessKotlinCheck
./gradlew -p sentry-samples/sentry-samples-android spotlessJavaCheck spotlessKotlinCheck

# Binary compatibility validator
api:
Expand All @@ -48,3 +49,7 @@ runUiTestCritical:
# Run tests and lint
check:
./gradlew check

# The Android sample is a separate build, so the root `check` doesn't reach it
checkSampleAndroid:
./gradlew -p sentry-samples/sentry-samples-android check
13 changes: 8 additions & 5 deletions sentry-samples/sentry-samples-android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ profiling) and integrations (Compose, OkHttp, SQLite, etc.).
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 the `sentry-samples/sentry-samples-android` directory in Android Studio and run the `sentry-samples-android` configuration.

The sample is a separate Gradle build that includes the SDK build, so changes to the SDK show up in the next build of the sample, with nothing to publish first.
The SDK build doesn't include the sample, so `./gradlew` from the repository root doesn't build, lint or format it.

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.
In Android Studio, add `useSagp=` (empty value) to the sample's `gradle.properties`, or pass `-PuseSagp` as a Gradle project property.

For LLM-driven verification, use `adb` to inspect and interact with the running emulator. Compose-based screens often expose stable
`Modifier.testTag()` values, which can be used with the UI hierarchy dump to locate controls reliably.
Expand Down Expand Up @@ -55,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
40 changes: 22 additions & 18 deletions sentry-samples/sentry-samples-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import org.gradle.internal.extensions.stdlib.capitalized
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion

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

val sentryVersion = libs.versions.sentry.get()

if (providers.gradleProperty("useSagp").isPresent) {
apply(plugin = "io.sentry.android.gradle")
}
Expand Down Expand Up @@ -53,7 +57,7 @@ android {
minSdk = 23
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 2
versionName = project.version.toString()
versionName = sentryVersion

externalNativeBuild {
cmake {
Expand Down Expand Up @@ -104,7 +108,7 @@ android {

signingConfigs {
getByName("debug") {
storeFile = rootProject.file("debug.keystore")
storeFile = file("../../debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
Expand Down Expand Up @@ -146,7 +150,7 @@ android {
val toggleNativeLoggingTask =
project.tasks.register<ToggleNativeLoggingTask>(taskName) {
mergedManifest.set(variant.artifacts.get(SingleArtifact.MERGED_MANIFEST))
rootDir.set(project.rootDir.absolutePath)
repoDir.set(layout.projectDirectory.dir("../..").asFile.canonicalPath)
}
project.afterEvaluate {
(variant as? VariantImpl<*>)?.taskContainer?.assembleTask?.configure {
Expand Down Expand Up @@ -175,19 +179,19 @@ sqldelight {
dependencies {
implementation(kotlin("stdlib-jdk8", getKotlinPluginVersion()))

implementation(projects.sentryAndroid)
implementation(projects.sentryAndroidFragment)
implementation(projects.sentryAndroidNavigation)
implementation(projects.sentryAndroidNavigation3)
implementation(projects.sentryAndroidSqlite)
implementation(projects.sentryAndroidTimber)
implementation(projects.sentryCompose)
implementation(projects.sentryKotlinExtensions)
implementation(projects.sentryOkhttp)
implementation(projects.sentrySpotlight)
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-navigation3:$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 @@ -228,15 +232,15 @@ dependencies {
ksp(libs.androidx.room.compiler)
ksp(libs.androidx.room3.compiler)

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

testImplementation(libs.google.truth)
testImplementation(libs.kotlin.test.junit)
}

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 @@ -254,7 +258,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
24 changes: 24 additions & 0 deletions sentry-samples/sentry-samples-android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Gradle properties are per build, so this repeats what the sample needs from the SDK build's
# gradle.properties. settings.gradle.kts reads the SDK version from that file.

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

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.defaults.buildfeatures.renderscript=false
android.defaults.buildfeatures.shaders=false
android.defaults.buildfeatures.aidl=false
android.defaults.buildfeatures.resvalues=false
android.nonTransitiveRClass=true

kotlin.stdlib.default.dependency=false

systemProp.org.gradle.internal.http.socketTimeout=120000
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
@@ -1,5 +1,7 @@
plugins {
id("com.android.test")
id("io.sentry.spotless")
id("io.sentry.jdk-target")
alias(libs.plugins.kotlin.android)
}

Expand All @@ -26,7 +28,8 @@ android {
}
}

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
77 changes: 77 additions & 0 deletions sentry-samples/sentry-samples-android/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// A standalone build that includes the SDK build, so it depends on the SDK through the same
// io.sentry:* coordinates an app would, and Gradle substitutes the SDK's projects for them.
// The SDK build doesn't include this one back, so its plugins and versions stay out of the SDK
// build. Run it with `./gradlew -p sentry-samples/sentry-samples-android <task>`.

pluginManagement {
repositories {
// Prefer local SAGP artifact if one exists; otherwise fall back to libs.versions.toml.
if (providers.gradleProperty("useSagp").isPresent) {
mavenLocal {
content {
includeGroup("io.sentry")
includeGroup("io.sentry.android.gradle")
}
}
}
mavenCentral()
gradlePluginPortal()
google()
}

// Every build in a composite must use the same AGP, so each build's settings applies the AGP
// matrix job's override.
System.getenv("VERSION_AGP")?.let { agpVersion ->
resolutionStrategy {
eachPlugin {
if (requested.id.id.startsWith("com.android")) {
useVersion(agpVersion)
}
}
}
}
}

plugins {
id("com.gradle.develocity") version "4.5.0"
id("com.gradle.common-custom-user-data-gradle-plugin") version "2.8.0"
}

develocity {
buildScan {
termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use")
termsOfUseAgree.set("yes")
}
}

// Gradle reads gradle.properties only for the build it belongs to, so read the SDK version from the
// SDK build's file.
val sentryVersion: String =
providers
.fileContents(layout.settingsDirectory.file("../../gradle.properties"))
.asText
.map { java.util.Properties().apply { load(it.reader()) }.getProperty("versionName") }
.get()

dependencyResolutionManagement {
repositories {
google()
mavenCentral()
mavenLocal()
}
versionCatalogs {
create("libs") {
from(files("../../gradle/libs.versions.toml"))
version("sentry", sentryVersion)
}
}
}

rootProject.name = "sentry-samples-android"

includeBuild("../..")

// For the io.sentry.spotless and io.sentry.jdk-target convention plugins.
includeBuild("../../build-logic")

include("macrobenchmark")
2 changes: 0 additions & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ include(
"sentry-reactor",
"sentry-async-profiler",
"sentry-ktor-client",
"sentry-samples:sentry-samples-android",
"sentry-samples:sentry-samples-console",
"sentry-samples:sentry-samples-console-otlp",
"sentry-samples:sentry-samples-console-opentelemetry-noagent",
Expand Down Expand Up @@ -141,7 +140,6 @@ include(
"sentry-samples:sentry-samples-spring-boot-4-webflux",
"sentry-samples:sentry-samples-netflix-dgs",
"sentry-android-integration-tests:sentry-uitest-android-critical",
"sentry-android-integration-tests:sentry-uitest-android-macrobenchmark",
"sentry-android-integration-tests:sentry-uitest-android",
"sentry-android-integration-tests:test-app-size",
"sentry-samples:sentry-samples-openfeign"
Expand Down
Loading