Skip to content
Open
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 .claude/skills/create-java-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ Derive the branch name from the changes being made. Use `feat/`, `fix/`, `ref/`,
## Step 2: Format Code and Regenerate API Files

```bash
./gradlew spotlessApply updateKotlinAbi
make format api
```

This is **required** before every PR in this repo. It formats all Java/Kotlin code via Spotless and regenerates the `.api` binary compatibility files.
This is **required** before every PR in this repo. It formats all Java/Kotlin code via Spotless, including the separate builds the root `spotlessApply` doesn't reach, and regenerates the `.api` binary compatibility files.

If the command fails, diagnose and fix the issue before continuing.

Expand Down
5 changes: 4 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ registries:
password: dummy # Required by dependabot
updates:
- package-ecosystem: "gradle"
directory: "/"
# The root build doesn't include the separate sample and test builds.
directories:
- "/"
- "/sentry-samples/sentry-samples-android"
registries:
- gradle-plugin-portal
schedule:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/format-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
uses: gradle/actions/setup-gradle@8da46b539cd8fe6ab27ca75128919a18d757604c # v6.4.1

- name: Format with spotlessApply
run: ./gradlew spotlessApply
run: make format

# 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
48 changes: 48 additions & 0 deletions .github/workflows/samples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: 'Samples'
# Runs `check` in each build the SDK build doesn't include, so a broken sample or
# integration test fails its own job instead of the SDK's Build job.
on:
push:
branches:
- main
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: Check ${{ matrix.name }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
include:
- name: sentry-samples-android
path: sentry-samples/sentry-samples-android

steps:
- name: Checkout Repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Setup Java Version
uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0
with:
distribution: 'temurin'
java-version: '21'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@8da46b539cd8fe6ab27ca75128919a18d757604c # v6.4.1

- name: Run Tests and Lint
run: ./gradlew -p ${{ matrix.path }} check

- name: Upload test results
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: test-results-${{ matrix.name }}
path: |
**/build/reports/*
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
11 changes: 7 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ The project uses **Gradle** with Kotlin DSL. Key build files:
## Essential Commands

```bash
# Format code and regenerate .api files (REQUIRED before committing)
./gradlew spotlessApply updateKotlinAbi
# Format code in every build and regenerate .api files (REQUIRED before committing)
make format api

# Run all tests and linter
./gradlew check
Expand All @@ -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 All @@ -74,7 +77,7 @@ resolves the per-module test task, including integration test suites, for you.
3. **Use todo tracking**: Work through todo items, marking them as complete as you go
4. **High-level communication**: Give high-level explanations of changes made, not step-by-step descriptions
5. **Simplicity first**: Make every task and code change as simple as possible. Avoid massive or complex changes. Impact as little code as possible.
6. **Format and regenerate**: Once done, format code and regenerate .api files: `./gradlew spotlessApply updateKotlinAbi`
6. **Format and regenerate**: Once done, format code and regenerate .api files: `make format api`
7. **Propose commit**: As final step, git stage relevant files and propose (but not execute) a single git commit command. This applies to implementation work; when the task is to open a PR, the `create-java-pr` skill takes over from here and does commit, push, and open it.

## Repository Skills
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
6 changes: 1 addition & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,7 @@ To format the changed code and make CI happy you can run:
make format
```

or

```shell
./gradlew spotlessApply
```
`./gradlew spotlessApply` alone formats only the SDK build, not the separate builds it doesn't include, such as the Android sample.

# Binary compatibility validation

Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleUiTestRelease assembleUiTestCriticalRelease runUiTestCritical check preMerge publish
.PHONY: all clean compile javadocs dryRelease update format checkFormat api assembleUiTestRelease assembleUiTestCriticalRelease runUiTestCritical check preMerge publish

all: stop clean javadocs compile
assembleUiTests: assembleUiTestRelease
Expand All @@ -25,9 +25,15 @@ dryRelease:
update:
./gradlew dependencyUpdates -Drevision=release

# Spotless formats the code. The root spotlessApply doesn't reach the separate builds.
format:
./gradlew spotlessApply
./gradlew -p sentry-samples/sentry-samples-android spotlessApply

# Spotless check's code
checkFormat:
./gradlew spotlessJavaCheck spotlessKotlinCheck
./gradlew -p sentry-samples/sentry-samples-android spotlessJavaCheck spotlessKotlinCheck

# Binary compatibility validator
api:
Expand Down
12 changes: 8 additions & 4 deletions sentry-samples/sentry-samples-android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ 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.
`gradle.properties`, `gradlew`, `gradle/wrapper` and `gradle/gradle-daemon-jvm.properties` are symlinks to the SDK build's, so both builds use the same Gradle, JDK and settings.

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 @@ -55,7 +59,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
1 change: 1 addition & 0 deletions sentry-samples/sentry-samples-android/gradle.properties
1 change: 1 addition & 0 deletions sentry-samples/sentry-samples-android/gradle/wrapper
1 change: 1 addition & 0 deletions sentry-samples/sentry-samples-android/gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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,7 @@ android {
}
}

targetProjectPath = ":sentry-samples:sentry-samples-android"
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