diff --git a/.claude/skills/create-java-pr/SKILL.md b/.claude/skills/create-java-pr/SKILL.md index 70bc4453a58..ef813f4b1c8 100644 --- a/.claude/skills/create-java-pr/SKILL.md +++ b/.claude/skills/create-java-pr/SKILL.md @@ -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. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2824699563c..8e712646a1e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,7 +7,11 @@ 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" + - "/sentry-android-integration-tests" registries: - gradle-plugin-portal schedule: diff --git a/.github/workflows/agp-matrix.yml b/.github/workflows/agp-matrix.yml index 81127cb468f..b55ee483720 100644 --- a/.github/workflows/agp-matrix.yml +++ b/.github/workflows/agp-matrix.yml @@ -85,7 +85,7 @@ jobs: disable-spellchecker: true emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disk-size: 4096M - script: ./gradlew sentry-android-integration-tests:sentry-uitest-android:connectedReleaseAndroidTest -Denvironment=github --daemon + script: ./gradlew -p sentry-android-integration-tests :sentry-uitest-android:connectedReleaseAndroidTest -Denvironment=github --daemon - name: Upload test results if: always() diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index fb1a1da7102..e55cddb74fd 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -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. diff --git a/.github/workflows/integration-tests-macrobenchmark.yml b/.github/workflows/integration-tests-macrobenchmark.yml index 654e8c17f76..eb1778adcd8 100644 --- a/.github/workflows/integration-tests-macrobenchmark.yml +++ b/.github/workflows/integration-tests-macrobenchmark.yml @@ -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: @@ -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 diff --git a/.github/workflows/integration-tests-size.yml b/.github/workflows/integration-tests-size.yml index 9705499f430..4254edcf4b2 100644 --- a/.github/workflows/integration-tests-size.yml +++ b/.github/workflows/integration-tests-size.yml @@ -29,6 +29,6 @@ jobs: uses: gradle/actions/setup-gradle@8da46b539cd8fe6ab27ca75128919a18d757604c # v6.4.1 - name: Size Analysis - run: ./gradlew :sentry-android-integration-tests:test-app-size:bundleRelease + run: ./gradlew -p sentry-android-integration-tests :test-app-size:bundleRelease env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} diff --git a/.github/workflows/samples.yml b/.github/workflows/samples.yml new file mode 100644 index 00000000000..c0376a85328 --- /dev/null +++ b/.github/workflows/samples.yml @@ -0,0 +1,50 @@ +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 + - name: sentry-android-integration-tests + path: sentry-android-integration-tests + + 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/* diff --git a/.sauce/sentry-uitest-android-macrobenchmark.yml b/.sauce/sentry-uitest-android-macrobenchmark.yml index 4e3d5cfbb3d..a6ef85e7c4a 100644 --- a/.sauce/sentry-uitest-android-macrobenchmark.yml +++ b/.sauce/sentry-uitest-android-macrobenchmark.yml @@ -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: diff --git a/AGENTS.md b/AGENTS.md index f447fc31512..ee6436f8d23 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 @@ -59,11 +59,14 @@ resolves the per-module test task, including integration test suites, for you. ### Android-Specific Commands ```bash -# Assemble Android test APKs -./gradlew :sentry-android-integration-tests:sentry-uitest-android:assembleRelease :sentry-android-integration-tests:sentry-uitest-android:assembleAndroidTest +# Assemble Android test APKs, in a separate Gradle build that includes this one +./gradlew -p sentry-android-integration-tests :sentry-uitest-android:assembleRelease :sentry-uitest-android:assembleAndroidTest # 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 @@ -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 @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 77714917518..d0642308ff8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/Makefile b/Makefile index c5bb6ac2313..d88f81d09ab 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -25,9 +25,17 @@ 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 + ./gradlew -p sentry-android-integration-tests spotlessApply + # Spotless check's code checkFormat: ./gradlew spotlessJavaCheck spotlessKotlinCheck + ./gradlew -p sentry-samples/sentry-samples-android spotlessJavaCheck spotlessKotlinCheck + ./gradlew -p sentry-android-integration-tests spotlessJavaCheck spotlessKotlinCheck # Binary compatibility validator api: @@ -35,11 +43,11 @@ api: # Assemble release and Android test apk of the uitest-android module assembleUiTestRelease: - ./gradlew :sentry-android-integration-tests:sentry-uitest-android:assembleRelease :sentry-android-integration-tests:sentry-uitest-android:assembleAndroidTest + ./gradlew -p sentry-android-integration-tests :sentry-uitest-android:assembleRelease :sentry-uitest-android:assembleAndroidTest # Assemble release of the uitest-android-critical module assembleUiTestCriticalRelease: - ./gradlew :sentry-android-integration-tests:sentry-uitest-android-critical:assembleRelease + ./gradlew -p sentry-android-integration-tests :sentry-uitest-android-critical:assembleRelease # Run Maestro tests for the uitest-android-critical module runUiTestCritical: diff --git a/build.gradle.kts b/build.gradle.kts index f880ab151b5..27a81c7a9a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,6 @@ plugins { alias(libs.plugins.gradle.versions) apply false 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 } @@ -34,9 +33,6 @@ buildscript { google() } dependencies { - // add classpath of sentry android gradle plugin - // classpath("io.sentry:sentry-android-gradle-plugin:{version}") - classpath(libs.commons.compress) } } diff --git a/gradle.properties b/gradle.properties index eeefd38c92d..417334e771c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,6 @@ 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.4.0 android.disableAgpUpgradePrompt=true diff --git a/sentry-android-integration-tests/README.md b/sentry-android-integration-tests/README.md index 4b3c495a978..2e84e1cbd06 100644 --- a/sentry-android-integration-tests/README.md +++ b/sentry-android-integration-tests/README.md @@ -1,4 +1,10 @@ # Android performance-impact and integration tests +This directory is a separate Gradle build that includes the SDK build, so run its tasks with +`./gradlew -p sentry-android-integration-tests `. The SDK build doesn't include it, so +`./gradlew` from the repository root doesn't build, lint or format these apps. +`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. + * [Espresso-based UI tests](./sentry-uitest-android) - run within SauceLabs (see /.sauce/*.yml) * Also used for compatibility test matrix against new AGP versions diff --git a/sentry-android-integration-tests/build.gradle.kts b/sentry-android-integration-tests/build.gradle.kts new file mode 100644 index 00000000000..53972e7fdb1 --- /dev/null +++ b/sentry-android-integration-tests/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.errorprone) apply false + alias(libs.plugins.detekt) apply false + alias(libs.plugins.gradle.versions) apply false + alias(libs.plugins.sentry) apply false +} diff --git a/sentry-android-integration-tests/gradle.properties b/sentry-android-integration-tests/gradle.properties new file mode 120000 index 00000000000..7677fb73be8 --- /dev/null +++ b/sentry-android-integration-tests/gradle.properties @@ -0,0 +1 @@ +../gradle.properties \ No newline at end of file diff --git a/sentry-android-integration-tests/gradle/gradle-daemon-jvm.properties b/sentry-android-integration-tests/gradle/gradle-daemon-jvm.properties new file mode 120000 index 00000000000..284e270637d --- /dev/null +++ b/sentry-android-integration-tests/gradle/gradle-daemon-jvm.properties @@ -0,0 +1 @@ +../../gradle/gradle-daemon-jvm.properties \ No newline at end of file diff --git a/sentry-android-integration-tests/gradle/wrapper b/sentry-android-integration-tests/gradle/wrapper new file mode 120000 index 00000000000..3232fe4e272 --- /dev/null +++ b/sentry-android-integration-tests/gradle/wrapper @@ -0,0 +1 @@ +../../gradle/wrapper \ No newline at end of file diff --git a/sentry-android-integration-tests/gradlew b/sentry-android-integration-tests/gradlew new file mode 120000 index 00000000000..502f5a2d3ec --- /dev/null +++ b/sentry-android-integration-tests/gradlew @@ -0,0 +1 @@ +../gradlew \ No newline at end of file diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts b/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts index 8a22b91890c..fc43a6ad062 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts @@ -5,8 +5,12 @@ plugins { id("com.android.application") alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) + id("io.sentry.spotless") + id("io.sentry.jdk-target") } +val sentryVersion = libs.versions.sentry.get() + android { compileSdk = libs.versions.compileSdk.get().toInt() compileSdkMinor = libs.versions.compileSdkMinor.get().toInt() @@ -47,7 +51,7 @@ dependencies { implementation(libs.androidx.compose.material3) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.core) - implementation(projects.sentryAndroidCore) + implementation("io.sentry:sentry-android-core:$sentryVersion") } tasks.withType().configureEach { diff --git a/sentry-android-integration-tests/sentry-uitest-android/README.md b/sentry-android-integration-tests/sentry-uitest-android/README.md index d6ccf2f1f93..9f6f57b3fc0 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/README.md +++ b/sentry-android-integration-tests/sentry-uitest-android/README.md @@ -5,13 +5,13 @@ By default the envelopes sent to relay are caught by a mock server which allows # How to use -Simply run `./gradlew connectedCheck` to run all ui tests of all modules (requires a connected device, either physical or an emulator). +Simply run `./gradlew -p sentry-android-integration-tests connectedCheck` to run all ui tests of all modules (requires a connected device, either physical or an emulator). # SauceLabs To run on saucelabs execute following commands (need also `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY` environment variables): For End 2 End: ``` -./gradlew :sentry-android-integration-tests:sentry-uitest-android:assembleRelease :sentry-android-integration-tests:sentry-uitest-android:assembleAndroidTest +./gradlew -p sentry-android-integration-tests :sentry-uitest-android:assembleRelease :sentry-uitest-android:assembleAndroidTest saucectl run -c .sauce/sentry-uitest-android-end2end.yml ``` diff --git a/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts b/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts index 53cc88fec46..4aa91bf3735 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts +++ b/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts @@ -9,8 +9,12 @@ plugins { alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) alias(libs.plugins.detekt) + id("io.sentry.spotless") + id("io.sentry.jdk-target") } +val sentryVersion = libs.versions.sentry.get() + android { compileSdk = libs.versions.compileSdk.get().toInt() compileSdkMinor = libs.versions.compileSdkMinor.get().toInt() @@ -46,7 +50,7 @@ android { signingConfigs { getByName("debug") { - storeFile = rootProject.file("debug.keystore") + storeFile = file("../../debug.keystore") storePassword = "android" keyAlias = "androiddebugkey" keyPassword = "android" @@ -90,10 +94,10 @@ dependencies { implementation(kotlin("stdlib-jdk8", getKotlinPluginVersion())) if (applySentryIntegrations) { - implementation(projects.sentryAndroid) - implementation(projects.sentryCompose) + implementation("io.sentry:sentry-android:$sentryVersion") + implementation("io.sentry:sentry-compose:$sentryVersion") } else { - implementation(projects.sentryAndroidCore) + implementation("io.sentry:sentry-android-core:$sentryVersion") } implementation(libs.androidx.appcompat) implementation(libs.androidx.activity.compose) @@ -112,7 +116,7 @@ dependencies { errorprone(libs.nullaway) androidTestUtil(libs.androidx.test.orchestrator) - androidTestImplementation(projects.sentryTestSupport) + androidTestImplementation("io.sentry:sentry-test-support:$sentryVersion") androidTestImplementation(libs.androidx.test.core.ktx) androidTestImplementation(libs.androidx.test.espresso.core) androidTestImplementation(libs.androidx.test.ext.junit) @@ -132,6 +136,12 @@ tasks.withType().configureEach { } } +detekt { + buildUponDefaultConfig = true + allRules = true + config.setFrom(file("../../detekt.yml")) +} + tasks.withType().configureEach { // Target version of the generated JVM bytecode. It is used for type resolution. jvmTarget = JavaVersion.VERSION_1_8.toString() diff --git a/sentry-android-integration-tests/settings.gradle.kts b/sentry-android-integration-tests/settings.gradle.kts new file mode 100644 index 00000000000..5a0d060f7d7 --- /dev/null +++ b/sentry-android-integration-tests/settings.gradle.kts @@ -0,0 +1,66 @@ +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.6.1" + 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") + } +} + +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + mavenLocal() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + // gradle.properties links to the SDK build's, so versionName is the SDK version. + version("sentry", providers.gradleProperty("versionName").get()) + } + } +} + +rootProject.name = "sentry-android-integration-tests" + +includeBuild("..") + +// For the io.sentry.spotless and io.sentry.jdk-target convention plugins. +includeBuild("../build-logic") + +include("sentry-uitest-android") +include("sentry-uitest-android-critical") +include("test-app-size") diff --git a/sentry-android-integration-tests/test-app-size/build.gradle.kts b/sentry-android-integration-tests/test-app-size/build.gradle.kts index 6260c761d53..678ae4103e0 100644 --- a/sentry-android-integration-tests/test-app-size/build.gradle.kts +++ b/sentry-android-integration-tests/test-app-size/build.gradle.kts @@ -1,8 +1,12 @@ plugins { id("com.android.application") id("io.sentry.android.gradle") + id("io.sentry.spotless") + id("io.sentry.jdk-target") } +val sentryVersion = libs.versions.sentry.get() + android { namespace = "io.sentry.tests.size" compileSdk = libs.versions.compileSdk.get().toInt() @@ -13,7 +17,7 @@ android { minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() versionCode = 1 - versionName = project.version.toString() + versionName = sentryVersion } buildTypes { @@ -42,12 +46,12 @@ configurations.configureEach { } dependencies { - implementation(projects.sentryAndroid) - implementation(projects.sentryAndroidTimber) - implementation(projects.sentryAndroidSqlite) - implementation(projects.sentryOkhttp) - implementation(projects.sentryCompose) - implementation(projects.sentryAndroidFragment) + implementation("io.sentry:sentry-android:$sentryVersion") + implementation("io.sentry:sentry-android-timber:$sentryVersion") + implementation("io.sentry:sentry-android-sqlite:$sentryVersion") + implementation("io.sentry:sentry-okhttp:$sentryVersion") + implementation("io.sentry:sentry-compose:$sentryVersion") + implementation("io.sentry:sentry-android-fragment:$sentryVersion") } sentry { diff --git a/sentry-samples/sentry-samples-android/README.md b/sentry-samples/sentry-samples-android/README.md index b065b91e955..2c2b328e43e 100644 --- a/sentry-samples/sentry-samples-android/README.md +++ b/sentry-samples/sentry-samples-android/README.md @@ -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. @@ -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 diff --git a/sentry-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts index 28d8748faa9..8fb29bb4ecd 100644 --- a/sentry-samples/sentry-samples-android/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/build.gradle.kts @@ -8,7 +8,9 @@ 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) @@ -16,6 +18,8 @@ plugins { alias(libs.plugins.sqldelight) } +val sentryVersion = libs.versions.sentry.get() + if (providers.gradleProperty("useSagp").isPresent) { apply(plugin = "io.sentry.android.gradle") } @@ -53,7 +57,7 @@ android { minSdk = 23 targetSdk = libs.versions.targetSdk.get().toInt() versionCode = 2 - versionName = project.version.toString() + versionName = sentryVersion externalNativeBuild { cmake { @@ -104,7 +108,7 @@ android { signingConfigs { getByName("debug") { - storeFile = rootProject.file("debug.keystore") + storeFile = file("../../debug.keystore") storePassword = "android" keyAlias = "androiddebugkey" keyPassword = "android" @@ -146,7 +150,7 @@ android { val toggleNativeLoggingTask = project.tasks.register(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 { @@ -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") @@ -228,7 +232,7 @@ 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) @@ -236,7 +240,7 @@ dependencies { } abstract class ToggleNativeLoggingTask : Exec() { - @get:Input abstract val rootDir: Property + @get:Input abstract val repoDir: Property @get:InputFile abstract val mergedManifest: RegularFileProperty @@ -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() diff --git a/sentry-samples/sentry-samples-android/gradle.properties b/sentry-samples/sentry-samples-android/gradle.properties new file mode 120000 index 00000000000..03ca90c515b --- /dev/null +++ b/sentry-samples/sentry-samples-android/gradle.properties @@ -0,0 +1 @@ +../../gradle.properties \ No newline at end of file diff --git a/sentry-samples/sentry-samples-android/gradle/gradle-daemon-jvm.properties b/sentry-samples/sentry-samples-android/gradle/gradle-daemon-jvm.properties new file mode 120000 index 00000000000..b6233ee4449 --- /dev/null +++ b/sentry-samples/sentry-samples-android/gradle/gradle-daemon-jvm.properties @@ -0,0 +1 @@ +../../../gradle/gradle-daemon-jvm.properties \ No newline at end of file diff --git a/sentry-samples/sentry-samples-android/gradle/wrapper b/sentry-samples/sentry-samples-android/gradle/wrapper new file mode 120000 index 00000000000..166ffc09a82 --- /dev/null +++ b/sentry-samples/sentry-samples-android/gradle/wrapper @@ -0,0 +1 @@ +../../../gradle/wrapper \ No newline at end of file diff --git a/sentry-samples/sentry-samples-android/gradlew b/sentry-samples/sentry-samples-android/gradlew new file mode 120000 index 00000000000..343e0d2caa4 --- /dev/null +++ b/sentry-samples/sentry-samples-android/gradlew @@ -0,0 +1 @@ +../../gradlew \ No newline at end of file diff --git a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/README.md b/sentry-samples/sentry-samples-android/macrobenchmark/README.md similarity index 97% rename from sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/README.md rename to sentry-samples/sentry-samples-android/macrobenchmark/README.md index 9914825584d..23c5fab6921 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/README.md +++ b/sentry-samples/sentry-samples-android/macrobenchmark/README.md @@ -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. @@ -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 diff --git a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build.gradle.kts b/sentry-samples/sentry-samples-android/macrobenchmark/build.gradle.kts similarity index 94% rename from sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build.gradle.kts rename to sentry-samples/sentry-samples-android/macrobenchmark/build.gradle.kts index cab7c28f934..a26fe088cd7 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/macrobenchmark/build.gradle.kts @@ -1,5 +1,7 @@ plugins { id("com.android.test") + id("io.sentry.spotless") + id("io.sentry.jdk-target") alias(libs.plugins.kotlin.android) } @@ -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 } diff --git a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/src/main/AndroidManifest.xml b/sentry-samples/sentry-samples-android/macrobenchmark/src/main/AndroidManifest.xml similarity index 100% rename from sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/src/main/AndroidManifest.xml rename to sentry-samples/sentry-samples-android/macrobenchmark/src/main/AndroidManifest.xml diff --git a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/src/main/java/io/sentry/uitest/android/macrobenchmark/SentryStartupBenchmark.kt b/sentry-samples/sentry-samples-android/macrobenchmark/src/main/java/io/sentry/uitest/android/macrobenchmark/SentryStartupBenchmark.kt similarity index 100% rename from sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/src/main/java/io/sentry/uitest/android/macrobenchmark/SentryStartupBenchmark.kt rename to sentry-samples/sentry-samples-android/macrobenchmark/src/main/java/io/sentry/uitest/android/macrobenchmark/SentryStartupBenchmark.kt diff --git a/sentry-samples/sentry-samples-android/settings.gradle.kts b/sentry-samples/sentry-samples-android/settings.gradle.kts new file mode 100644 index 00000000000..9d024bf958a --- /dev/null +++ b/sentry-samples/sentry-samples-android/settings.gradle.kts @@ -0,0 +1,64 @@ +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.6.1" + 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") + } +} + +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + mavenLocal() + } + versionCatalogs { + create("libs") { + from(files("../../gradle/libs.versions.toml")) + // gradle.properties links to the SDK build's, so versionName is the SDK version. + version("sentry", providers.gradleProperty("versionName").get()) + } + } +} + +rootProject.name = "sentry-samples-android" + +includeBuild("../..") + +// For the io.sentry.spotless and io.sentry.jdk-target convention plugins. +includeBuild("../../build-logic") + +include("macrobenchmark") diff --git a/settings.gradle.kts b/settings.gradle.kts index 2849794723f..aae18edb922 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,15 +2,6 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 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() @@ -112,7 +103,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", @@ -140,9 +130,5 @@ include( "sentry-samples:sentry-samples-spring-boot-4-otlp", "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" )