From 35bf18d25afa724bfa8473fc3e4b40d79902e4a2 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 3 Mar 2026 11:20:41 +0100 Subject: [PATCH 01/12] feat(snapshots): Add snapshot upload task Add a new `sentryUploadSnapshots` task that uploads snapshots to Sentry via `sentry-cli build snapshots`. The task is registered when the user configures a snapshots path via the new `sentry.snapshots` extension block. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../io/sentry/android/gradle/SentryPlugin.kt | 19 +++ .../extensions/SentryPluginExtension.kt | 8 + .../gradle/extensions/SnapshotsExtension.kt | 13 ++ .../gradle/tasks/SentryUploadSnapshotsTask.kt | 67 +++++++++ .../tasks/SentryUploadSnapshotsTaskTest.kt | 138 ++++++++++++++++++ 5 files changed, 245 insertions(+) create mode 100644 plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt create mode 100644 plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt create mode 100644 plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt index 2582c78f2..7784770df 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt @@ -4,6 +4,7 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension import io.sentry.BuildConfig import io.sentry.android.gradle.autoinstall.installDependencies import io.sentry.android.gradle.extensions.SentryPluginExtension +import io.sentry.android.gradle.tasks.SentryUploadSnapshotsTask import io.sentry.android.gradle.util.AgpVersions import java.io.File import javax.inject.Inject @@ -65,6 +66,24 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi sentryProjectParameter, ) + project.afterEvaluate { + if (extension.snapshots.path.isPresent) { + SentryUploadSnapshotsTask.register( + project = project, + extension = extension, + sentryTelemetryProvider = null, + debug = extension.debug, + cliExecutable = cliExecutable, + sentryOrg = sentryOrgParameter?.let { project.provider { it } } ?: extension.org, + sentryProject = + sentryProjectParameter?.let { project.provider { it } } ?: extension.projectName, + sentryAuthToken = extension.authToken, + sentryUrl = extension.url, + snapshotsPath = extension.snapshots.path, + ) + } + } + project.installDependencies(extension, true) } } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SentryPluginExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SentryPluginExtension.kt index 377e1a15f..6d9ca9c61 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SentryPluginExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SentryPluginExtension.kt @@ -127,6 +127,14 @@ abstract class SentryPluginExtension @Inject constructor(project: Project) { vcsInfoAction.execute(vcsInfo) } + val snapshots: SnapshotsExtension = objects.newInstance(SnapshotsExtension::class.java) + + /** Configure the snapshots upload. */ + @Experimental + fun snapshots(snapshotsAction: Action) { + snapshotsAction.execute(snapshots) + } + /** * Disables or enables the reporting of dependencies metadata for Sentry. If enabled the plugin * will collect external dependencies and will take care of uploading them to Sentry as part of diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt new file mode 100644 index 000000000..85309e1dc --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt @@ -0,0 +1,13 @@ +package io.sentry.android.gradle.extensions + +import javax.inject.Inject +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.jetbrains.annotations.ApiStatus.Experimental + +@Experimental +open class SnapshotsExtension @Inject constructor(objects: ObjectFactory) { + + /** The path to the folder containing snapshots to upload. */ + val path: DirectoryProperty = objects.directoryProperty() +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt new file mode 100644 index 000000000..4ee63b66e --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt @@ -0,0 +1,67 @@ +package io.sentry.android.gradle.tasks + +import io.sentry.android.gradle.autoinstall.SENTRY_GROUP +import io.sentry.android.gradle.extensions.SentryPluginExtension +import io.sentry.android.gradle.telemetry.SentryTelemetryService +import io.sentry.android.gradle.telemetry.withSentryTelemetry +import io.sentry.android.gradle.util.asSentryCliExec +import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskProvider +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "Uploads should not be cached") +abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { + + init { + group = SENTRY_GROUP + description = "Uploads snapshots to Sentry" + } + + @get:InputDirectory + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val snapshotsPath: DirectoryProperty + + override fun getArguments(args: MutableList) { + args.add("build") + args.add("snapshots") + args.add(snapshotsPath.get().asFile.absolutePath) + } + + companion object { + fun register( + project: Project, + extension: SentryPluginExtension, + sentryTelemetryProvider: Provider?, + debug: Property, + cliExecutable: Provider, + sentryOrg: Provider, + sentryProject: Provider, + sentryAuthToken: Property, + sentryUrl: Property, + snapshotsPath: DirectoryProperty, + ): TaskProvider { + return project.tasks.register( + "sentryUploadSnapshots", + SentryUploadSnapshotsTask::class.java, + ) { task -> + task.workingDir(project.rootDir) + task.debug.set(debug) + task.cliExecutable.set(cliExecutable) + task.sentryOrganization.set(sentryOrg) + task.sentryProject.set(sentryProject) + task.sentryAuthToken.set(sentryAuthToken) + task.sentryUrl.set(sentryUrl) + task.snapshotsPath.set(snapshotsPath) + task.sentryTelemetryService.set(sentryTelemetryProvider) + task.asSentryCliExec() + task.withSentryTelemetry(extension, sentryTelemetryProvider) + } + } + } +} diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt new file mode 100644 index 000000000..b3369d24d --- /dev/null +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt @@ -0,0 +1,138 @@ +package io.sentry.android.gradle.tasks + +import java.io.File +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.junit.Test + +class SentryUploadSnapshotsTaskTest { + + @Test + fun `cli-executable is set correctly`() { + val task = + createTestTask { + it.cliExecutable.set("sentry-cli") + it.snapshotsPath.set(File("/path/to/snapshots")) + } + + val args = task.computeCommandLineArgs() + + assertTrue("sentry-cli" in args) + assertTrue("build" in args) + assertTrue("snapshots" in args) + assertTrue("/path/to/snapshots" in args) + assertFalse("--log-level=debug" in args) + } + + @Test + fun `--log-level=debug is set correctly`() { + val task = + createTestTask { + it.cliExecutable.set("sentry-cli") + it.snapshotsPath.set(File("/path/to/snapshots")) + it.debug.set(true) + } + + val args = task.computeCommandLineArgs() + + assertTrue("--log-level=debug" in args) + } + + @Test + fun `with sentryProperties file SENTRY_PROPERTIES is set correctly`() { + val project = createProject() + val propertiesFile = project.file("dummy/folder/sentry.properties") + val task = createTestTask(project) { it.sentryProperties.set(propertiesFile) } + + task.setSentryPropertiesEnv() + + assertEquals(propertiesFile.absolutePath, task.environment["SENTRY_PROPERTIES"].toString()) + } + + @Test + fun `without sentryProperties file SENTRY_PROPERTIES is not set`() { + val task = createTestTask() + + task.setSentryPropertiesEnv() + + assertNull(task.environment["SENTRY_PROPERTIES"]) + } + + @Test + fun `with sentryOrganization adds --org`() { + val task = + createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryOrganization.set("dummy-org") + it.snapshotsPath.set(File("/path/to/snapshots")) + } + + val args = task.computeCommandLineArgs() + + assertTrue("--org" in args) + assertTrue("dummy-org" in args) + } + + @Test + fun `with sentryProject adds --project`() { + val task = + createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryProject.set("dummy-proj") + it.snapshotsPath.set(File("/path/to/snapshots")) + } + + val args = task.computeCommandLineArgs() + + assertTrue("--project" in args) + assertTrue("dummy-proj" in args) + } + + @Test + fun `with sentryUrl adds --url`() { + val task = + createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryUrl.set("https://some-host.sentry.io") + it.snapshotsPath.set(File("/path/to/snapshots")) + } + + val args = task.computeCommandLineArgs() + + assertTrue("--url" in args) + assertTrue("https://some-host.sentry.io" in args) + } + + @Test + fun `the --url parameter is placed as the first argument`() { + val task = + createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryUrl.set("https://some-host.sentry.io") + it.snapshotsPath.set(File("/path/to/snapshots")) + } + + val args = task.computeCommandLineArgs() + + assertEquals(1, args.indexOf("--url")) + } + + private fun createProject(): Project { + with(ProjectBuilder.builder().build()) { + plugins.apply("io.sentry.android.gradle") + return this + } + } + + private fun createTestTask( + project: Project = createProject(), + block: (SentryUploadSnapshotsTask) -> Unit = {}, + ): SentryUploadSnapshotsTask = + project.tasks + .register("testUploadSnapshots", SentryUploadSnapshotsTask::class.java) { block(it) } + .get() +} From ae87b2585715d38465d8af22c0fdf3c6901141a7 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 3 Mar 2026 17:07:28 +0100 Subject: [PATCH 02/12] fix(snapshots): Wire telemetry provider for snapshot uploads Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt index 7784770df..13733d435 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt @@ -5,6 +5,7 @@ import io.sentry.BuildConfig import io.sentry.android.gradle.autoinstall.installDependencies import io.sentry.android.gradle.extensions.SentryPluginExtension import io.sentry.android.gradle.tasks.SentryUploadSnapshotsTask +import io.sentry.android.gradle.telemetry.SentryTelemetryService import io.sentry.android.gradle.util.AgpVersions import java.io.File import javax.inject.Inject @@ -68,10 +69,11 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi project.afterEvaluate { if (extension.snapshots.path.isPresent) { + val sentryTelemetryProvider = SentryTelemetryService.register(project) SentryUploadSnapshotsTask.register( project = project, extension = extension, - sentryTelemetryProvider = null, + sentryTelemetryProvider = sentryTelemetryProvider, debug = extension.debug, cliExecutable = cliExecutable, sentryOrg = sentryOrgParameter?.let { project.provider { it } } ?: extension.org, From 1eec8e8e0da3a0d0c7b7e5a17dc2be7fd147ed6e Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 3 Mar 2026 17:13:56 +0100 Subject: [PATCH 03/12] refactor(snapshots): Remove afterEvaluate and isPresent check Register the task unconditionally and let the lazy DirectoryProperty handle resolution at execution time. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../io/sentry/android/gradle/SentryPlugin.kt | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt index 13733d435..4b5ffc1e3 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt @@ -67,24 +67,20 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi sentryProjectParameter, ) - project.afterEvaluate { - if (extension.snapshots.path.isPresent) { - val sentryTelemetryProvider = SentryTelemetryService.register(project) - SentryUploadSnapshotsTask.register( - project = project, - extension = extension, - sentryTelemetryProvider = sentryTelemetryProvider, - debug = extension.debug, - cliExecutable = cliExecutable, - sentryOrg = sentryOrgParameter?.let { project.provider { it } } ?: extension.org, - sentryProject = - sentryProjectParameter?.let { project.provider { it } } ?: extension.projectName, - sentryAuthToken = extension.authToken, - sentryUrl = extension.url, - snapshotsPath = extension.snapshots.path, - ) - } - } + val sentryTelemetryProvider = SentryTelemetryService.register(project) + SentryUploadSnapshotsTask.register( + project = project, + extension = extension, + sentryTelemetryProvider = sentryTelemetryProvider, + debug = extension.debug, + cliExecutable = cliExecutable, + sentryOrg = sentryOrgParameter?.let { project.provider { it } } ?: extension.org, + sentryProject = + sentryProjectParameter?.let { project.provider { it } } ?: extension.projectName, + sentryAuthToken = extension.authToken, + sentryUrl = extension.url, + snapshotsPath = extension.snapshots.path, + ) project.installDependencies(extension, true) } From ca2fec860b8c9239d530352f450742c1bdff8407 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 4 Mar 2026 08:34:10 +0100 Subject: [PATCH 04/12] feat(snapshots): Add required --app-id argument sentry-cli build snapshots requires --app-id. Add it as a required property on the SnapshotsExtension. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../main/kotlin/io/sentry/android/gradle/SentryPlugin.kt | 1 + .../android/gradle/extensions/SnapshotsExtension.kt | 4 ++++ .../android/gradle/tasks/SentryUploadSnapshotsTask.kt | 7 +++++++ .../android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt | 8 ++++++++ 4 files changed, 20 insertions(+) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt index 4b5ffc1e3..31c03f891 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt @@ -79,6 +79,7 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi sentryProjectParameter?.let { project.provider { it } } ?: extension.projectName, sentryAuthToken = extension.authToken, sentryUrl = extension.url, + appId = extension.snapshots.appId, snapshotsPath = extension.snapshots.path, ) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt index 85309e1dc..01542c7a7 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt @@ -3,11 +3,15 @@ package io.sentry.android.gradle.extensions import javax.inject.Inject import org.gradle.api.file.DirectoryProperty import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.jetbrains.annotations.ApiStatus.Experimental @Experimental open class SnapshotsExtension @Inject constructor(objects: ObjectFactory) { + /** The application identifier used to associate snapshots with an app. */ + val appId: Property = objects.property(String::class.java) + /** The path to the folder containing snapshots to upload. */ val path: DirectoryProperty = objects.directoryProperty() } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt index 4ee63b66e..89020b899 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt @@ -9,6 +9,7 @@ import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity @@ -23,6 +24,8 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { description = "Uploads snapshots to Sentry" } + @get:Input abstract val appId: Property + @get:InputDirectory @get:PathSensitive(PathSensitivity.RELATIVE) abstract val snapshotsPath: DirectoryProperty @@ -30,6 +33,8 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { override fun getArguments(args: MutableList) { args.add("build") args.add("snapshots") + args.add("--app-id") + args.add(appId.get()) args.add(snapshotsPath.get().asFile.absolutePath) } @@ -44,6 +49,7 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { sentryProject: Provider, sentryAuthToken: Property, sentryUrl: Property, + appId: Property, snapshotsPath: DirectoryProperty, ): TaskProvider { return project.tasks.register( @@ -57,6 +63,7 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { task.sentryProject.set(sentryProject) task.sentryAuthToken.set(sentryAuthToken) task.sentryUrl.set(sentryUrl) + task.appId.set(appId) task.snapshotsPath.set(snapshotsPath) task.sentryTelemetryService.set(sentryTelemetryProvider) task.asSentryCliExec() diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt index b3369d24d..59bbc812b 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt @@ -16,6 +16,7 @@ class SentryUploadSnapshotsTaskTest { val task = createTestTask { it.cliExecutable.set("sentry-cli") + it.appId.set("com.example") it.snapshotsPath.set(File("/path/to/snapshots")) } @@ -24,6 +25,8 @@ class SentryUploadSnapshotsTaskTest { assertTrue("sentry-cli" in args) assertTrue("build" in args) assertTrue("snapshots" in args) + assertTrue("--app-id" in args) + assertTrue("com.example" in args) assertTrue("/path/to/snapshots" in args) assertFalse("--log-level=debug" in args) } @@ -33,6 +36,7 @@ class SentryUploadSnapshotsTaskTest { val task = createTestTask { it.cliExecutable.set("sentry-cli") + it.appId.set("com.example") it.snapshotsPath.set(File("/path/to/snapshots")) it.debug.set(true) } @@ -68,6 +72,7 @@ class SentryUploadSnapshotsTaskTest { createTestTask { it.cliExecutable.set("sentry-cli") it.sentryOrganization.set("dummy-org") + it.appId.set("com.example") it.snapshotsPath.set(File("/path/to/snapshots")) } @@ -83,6 +88,7 @@ class SentryUploadSnapshotsTaskTest { createTestTask { it.cliExecutable.set("sentry-cli") it.sentryProject.set("dummy-proj") + it.appId.set("com.example") it.snapshotsPath.set(File("/path/to/snapshots")) } @@ -98,6 +104,7 @@ class SentryUploadSnapshotsTaskTest { createTestTask { it.cliExecutable.set("sentry-cli") it.sentryUrl.set("https://some-host.sentry.io") + it.appId.set("com.example") it.snapshotsPath.set(File("/path/to/snapshots")) } @@ -113,6 +120,7 @@ class SentryUploadSnapshotsTaskTest { createTestTask { it.cliExecutable.set("sentry-cli") it.sentryUrl.set("https://some-host.sentry.io") + it.appId.set("com.example") it.snapshotsPath.set(File("/path/to/snapshots")) } From 2e69dd4e3446be08d5178661f0e321696c48dfdd Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 4 Mar 2026 08:39:40 +0100 Subject: [PATCH 05/12] docs: Add changelog entry for snapshot uploads Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b725d225..9b09163bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- (Experimental) Add snapshot upload support via `sentryUploadSnapshots` task ([#1091](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1091)) + ### Dependencies - Bump CLI from v3.2.0 to v3.2.3 ([#1084](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1084), [#1090](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1090)) From 582082ec5d75c57fab634125df1948fd1420e016 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 4 Mar 2026 08:47:04 +0100 Subject: [PATCH 06/12] refactor(snapshots): Simplify task registration API Move all wiring logic into the task's register() method so the plugin call site only passes project, extension, and org/project overrides. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../io/sentry/android/gradle/SentryPlugin.kt | 14 ++------ .../gradle/tasks/SentryUploadSnapshotsTask.kt | 34 +++++++++---------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt index 31c03f891..d5e2b4f0f 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt @@ -5,7 +5,6 @@ import io.sentry.BuildConfig import io.sentry.android.gradle.autoinstall.installDependencies import io.sentry.android.gradle.extensions.SentryPluginExtension import io.sentry.android.gradle.tasks.SentryUploadSnapshotsTask -import io.sentry.android.gradle.telemetry.SentryTelemetryService import io.sentry.android.gradle.util.AgpVersions import java.io.File import javax.inject.Inject @@ -67,20 +66,11 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi sentryProjectParameter, ) - val sentryTelemetryProvider = SentryTelemetryService.register(project) SentryUploadSnapshotsTask.register( project = project, extension = extension, - sentryTelemetryProvider = sentryTelemetryProvider, - debug = extension.debug, - cliExecutable = cliExecutable, - sentryOrg = sentryOrgParameter?.let { project.provider { it } } ?: extension.org, - sentryProject = - sentryProjectParameter?.let { project.provider { it } } ?: extension.projectName, - sentryAuthToken = extension.authToken, - sentryUrl = extension.url, - appId = extension.snapshots.appId, - snapshotsPath = extension.snapshots.path, + sentryOrgOverride = sentryOrgParameter, + sentryProjectOverride = sentryProjectParameter, ) project.installDependencies(extension, true) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt index 89020b899..195e834c6 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt @@ -1,6 +1,7 @@ package io.sentry.android.gradle.tasks import io.sentry.android.gradle.autoinstall.SENTRY_GROUP +import io.sentry.android.gradle.cliExecutableProvider import io.sentry.android.gradle.extensions.SentryPluginExtension import io.sentry.android.gradle.telemetry.SentryTelemetryService import io.sentry.android.gradle.telemetry.withSentryTelemetry @@ -8,7 +9,6 @@ import io.sentry.android.gradle.util.asSentryCliExec import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.PathSensitive @@ -42,29 +42,27 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { fun register( project: Project, extension: SentryPluginExtension, - sentryTelemetryProvider: Provider?, - debug: Property, - cliExecutable: Provider, - sentryOrg: Provider, - sentryProject: Provider, - sentryAuthToken: Property, - sentryUrl: Property, - appId: Property, - snapshotsPath: DirectoryProperty, + sentryOrgOverride: String?, + sentryProjectOverride: String?, ): TaskProvider { + val sentryTelemetryProvider = SentryTelemetryService.register(project) return project.tasks.register( "sentryUploadSnapshots", SentryUploadSnapshotsTask::class.java, ) { task -> task.workingDir(project.rootDir) - task.debug.set(debug) - task.cliExecutable.set(cliExecutable) - task.sentryOrganization.set(sentryOrg) - task.sentryProject.set(sentryProject) - task.sentryAuthToken.set(sentryAuthToken) - task.sentryUrl.set(sentryUrl) - task.appId.set(appId) - task.snapshotsPath.set(snapshotsPath) + task.debug.set(extension.debug) + task.cliExecutable.set(project.cliExecutableProvider()) + task.sentryOrganization.set( + sentryOrgOverride?.let { project.provider { it } } ?: extension.org + ) + task.sentryProject.set( + sentryProjectOverride?.let { project.provider { it } } ?: extension.projectName + ) + task.sentryAuthToken.set(extension.authToken) + task.sentryUrl.set(extension.url) + task.appId.set(extension.snapshots.appId) + task.snapshotsPath.set(extension.snapshots.path) task.sentryTelemetryService.set(sentryTelemetryProvider) task.asSentryCliExec() task.withSentryTelemetry(extension, sentryTelemetryProvider) From cfa43366e6abc2fab17d775c63b0028bb38f8ac0 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 4 Mar 2026 09:02:11 +0100 Subject: [PATCH 07/12] style: Apply spotless formatting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../tasks/SentryUploadSnapshotsTaskTest.kt | 76 +++++++++---------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt index 59bbc812b..5955b08bf 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTaskTest.kt @@ -13,12 +13,11 @@ class SentryUploadSnapshotsTaskTest { @Test fun `cli-executable is set correctly`() { - val task = - createTestTask { - it.cliExecutable.set("sentry-cli") - it.appId.set("com.example") - it.snapshotsPath.set(File("/path/to/snapshots")) - } + val task = createTestTask { + it.cliExecutable.set("sentry-cli") + it.appId.set("com.example") + it.snapshotsPath.set(File("/path/to/snapshots")) + } val args = task.computeCommandLineArgs() @@ -33,13 +32,12 @@ class SentryUploadSnapshotsTaskTest { @Test fun `--log-level=debug is set correctly`() { - val task = - createTestTask { - it.cliExecutable.set("sentry-cli") - it.appId.set("com.example") - it.snapshotsPath.set(File("/path/to/snapshots")) - it.debug.set(true) - } + val task = createTestTask { + it.cliExecutable.set("sentry-cli") + it.appId.set("com.example") + it.snapshotsPath.set(File("/path/to/snapshots")) + it.debug.set(true) + } val args = task.computeCommandLineArgs() @@ -68,13 +66,12 @@ class SentryUploadSnapshotsTaskTest { @Test fun `with sentryOrganization adds --org`() { - val task = - createTestTask { - it.cliExecutable.set("sentry-cli") - it.sentryOrganization.set("dummy-org") - it.appId.set("com.example") - it.snapshotsPath.set(File("/path/to/snapshots")) - } + val task = createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryOrganization.set("dummy-org") + it.appId.set("com.example") + it.snapshotsPath.set(File("/path/to/snapshots")) + } val args = task.computeCommandLineArgs() @@ -84,13 +81,12 @@ class SentryUploadSnapshotsTaskTest { @Test fun `with sentryProject adds --project`() { - val task = - createTestTask { - it.cliExecutable.set("sentry-cli") - it.sentryProject.set("dummy-proj") - it.appId.set("com.example") - it.snapshotsPath.set(File("/path/to/snapshots")) - } + val task = createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryProject.set("dummy-proj") + it.appId.set("com.example") + it.snapshotsPath.set(File("/path/to/snapshots")) + } val args = task.computeCommandLineArgs() @@ -100,13 +96,12 @@ class SentryUploadSnapshotsTaskTest { @Test fun `with sentryUrl adds --url`() { - val task = - createTestTask { - it.cliExecutable.set("sentry-cli") - it.sentryUrl.set("https://some-host.sentry.io") - it.appId.set("com.example") - it.snapshotsPath.set(File("/path/to/snapshots")) - } + val task = createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryUrl.set("https://some-host.sentry.io") + it.appId.set("com.example") + it.snapshotsPath.set(File("/path/to/snapshots")) + } val args = task.computeCommandLineArgs() @@ -116,13 +111,12 @@ class SentryUploadSnapshotsTaskTest { @Test fun `the --url parameter is placed as the first argument`() { - val task = - createTestTask { - it.cliExecutable.set("sentry-cli") - it.sentryUrl.set("https://some-host.sentry.io") - it.appId.set("com.example") - it.snapshotsPath.set(File("/path/to/snapshots")) - } + val task = createTestTask { + it.cliExecutable.set("sentry-cli") + it.sentryUrl.set("https://some-host.sentry.io") + it.appId.set("com.example") + it.snapshotsPath.set(File("/path/to/snapshots")) + } val args = task.computeCommandLineArgs() From eb5354329ca25ca65113a5e5027ab38ac985a071 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 5 Mar 2026 15:00:23 +0100 Subject: [PATCH 08/12] refactor(snapshots): Wire telemetry and sentry properties properly Add proper telemetry setup via configureTelemetryNoVariant, wire sentryProperties file, and pass buildEvents and cliExecutable to the register method. Remove unused GradleInternal import. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../io/sentry/android/gradle/SentryPlugin.kt | 10 +++-- .../gradle/tasks/SentryUploadSnapshotsTask.kt | 44 +++++++++++++++++-- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt index d5e2b4f0f..727545978 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt @@ -67,10 +67,12 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi ) SentryUploadSnapshotsTask.register( - project = project, - extension = extension, - sentryOrgOverride = sentryOrgParameter, - sentryProjectOverride = sentryProjectParameter, + project, + extension, + buildEvents, + cliExecutable, + sentryOrgParameter, + sentryProjectParameter, ) project.installDependencies(extension, true) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt index 195e834c6..d15d6dc66 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt @@ -1,7 +1,7 @@ package io.sentry.android.gradle.tasks +import io.sentry.android.gradle.SentryPropertiesFileProvider.getPropertiesFilePath import io.sentry.android.gradle.autoinstall.SENTRY_GROUP -import io.sentry.android.gradle.cliExecutableProvider import io.sentry.android.gradle.extensions.SentryPluginExtension import io.sentry.android.gradle.telemetry.SentryTelemetryService import io.sentry.android.gradle.telemetry.withSentryTelemetry @@ -9,11 +9,13 @@ import io.sentry.android.gradle.util.asSentryCliExec import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskProvider +import org.gradle.internal.build.event.BuildEventListenerRegistryInternal import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Uploads should not be cached") @@ -42,17 +44,29 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { fun register( project: Project, extension: SentryPluginExtension, + buildEvents: BuildEventListenerRegistryInternal, + cliExecutable: Provider, sentryOrgOverride: String?, sentryProjectOverride: String?, ): TaskProvider { - val sentryTelemetryProvider = SentryTelemetryService.register(project) + val sentryTelemetryProvider = + configureTelemetryNoVariant( + project, + extension, + cliExecutable, + sentryOrgOverride, + buildEvents, + ) return project.tasks.register( "sentryUploadSnapshots", SentryUploadSnapshotsTask::class.java, ) { task -> task.workingDir(project.rootDir) task.debug.set(extension.debug) - task.cliExecutable.set(project.cliExecutableProvider()) + task.cliExecutable.set(cliExecutable) + task.sentryProperties.set( + getPropertiesFilePath(project)?.let { file -> project.file(file) } + ) task.sentryOrganization.set( sentryOrgOverride?.let { project.provider { it } } ?: extension.org ) @@ -70,3 +84,27 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { } } } + +private fun configureTelemetryNoVariant( + project: Project, + extension: SentryPluginExtension, + cliExecutable: Provider, + sentryOrg: String?, + buildEvents: BuildEventListenerRegistryInternal, +): Provider { + val sentryTelemetryProvider = SentryTelemetryService.register(project) + project.gradle.taskGraph.whenReady { + sentryTelemetryProvider.get().start { + SentryTelemetryService.createParameters( + project, + null, + extension, + cliExecutable, + sentryOrg, + "Android", + ) + } + buildEvents.onOperationCompletion(sentryTelemetryProvider) + } + return sentryTelemetryProvider +} From 65587af372cc1d45261a6a48fb024b68cc93b927 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 5 Mar 2026 15:05:39 +0100 Subject: [PATCH 09/12] fix(snapshots): Make variant nullable in getPropertiesFilePath The snapshot upload task has no variant context, so variant needs to be optional when resolving the sentry.properties file path. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../android/gradle/SentryPropertiesFileProvider.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt index e74473510..88f04d623 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt @@ -18,9 +18,9 @@ internal object SentryPropertiesFileProvider { * @return A [String] for the path if sentry.properties is found or null otherwise */ @JvmStatic - fun getPropertiesFilePath(project: Project, variant: SentryVariant): String? { - val flavorName = variant.flavorName.orEmpty() - val buildTypeName = variant.buildTypeName.orEmpty() + fun getPropertiesFilePath(project: Project, variant: SentryVariant? = null): String? { + val flavorName = variant?.flavorName.orEmpty() + val buildTypeName = variant?.buildTypeName.orEmpty() val projDir = project.projectDir val rootDir = project.rootDir @@ -40,7 +40,8 @@ internal object SentryPropertiesFileProvider { // Other flavors dirs possibleFiles.addAll( - variant.productFlavors.map { "${projDir}${sep}src${sep}${it}${sep}$FILENAME" } + variant?.productFlavors?.map { "${projDir}${sep}src${sep}${it}${sep}$FILENAME" } + ?: emptyList() ) // Root project dirs From b298f89d927be203fd531f96e9a35245f66392b0 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 5 Mar 2026 16:31:15 +0100 Subject: [PATCH 10/12] refactor(snapshots): Remove telemetry from snapshot uploads Telemetry wiring is not needed for the snapshot upload task. Remove the unused buildEvents parameter and the configureTelemetryNoVariant helper function. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../io/sentry/android/gradle/SentryPlugin.kt | 1 - .../gradle/tasks/SentryUploadSnapshotsTask.kt | 38 ------------------- 2 files changed, 39 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt index 727545978..838a48efc 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt @@ -69,7 +69,6 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi SentryUploadSnapshotsTask.register( project, extension, - buildEvents, cliExecutable, sentryOrgParameter, sentryProjectParameter, diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt index d15d6dc66..6a0620921 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt @@ -3,8 +3,6 @@ package io.sentry.android.gradle.tasks import io.sentry.android.gradle.SentryPropertiesFileProvider.getPropertiesFilePath import io.sentry.android.gradle.autoinstall.SENTRY_GROUP import io.sentry.android.gradle.extensions.SentryPluginExtension -import io.sentry.android.gradle.telemetry.SentryTelemetryService -import io.sentry.android.gradle.telemetry.withSentryTelemetry import io.sentry.android.gradle.util.asSentryCliExec import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty @@ -15,7 +13,6 @@ import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskProvider -import org.gradle.internal.build.event.BuildEventListenerRegistryInternal import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Uploads should not be cached") @@ -44,19 +41,10 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { fun register( project: Project, extension: SentryPluginExtension, - buildEvents: BuildEventListenerRegistryInternal, cliExecutable: Provider, sentryOrgOverride: String?, sentryProjectOverride: String?, ): TaskProvider { - val sentryTelemetryProvider = - configureTelemetryNoVariant( - project, - extension, - cliExecutable, - sentryOrgOverride, - buildEvents, - ) return project.tasks.register( "sentryUploadSnapshots", SentryUploadSnapshotsTask::class.java, @@ -77,34 +65,8 @@ abstract class SentryUploadSnapshotsTask : SentryCliExecTask() { task.sentryUrl.set(extension.url) task.appId.set(extension.snapshots.appId) task.snapshotsPath.set(extension.snapshots.path) - task.sentryTelemetryService.set(sentryTelemetryProvider) task.asSentryCliExec() - task.withSentryTelemetry(extension, sentryTelemetryProvider) } } } } - -private fun configureTelemetryNoVariant( - project: Project, - extension: SentryPluginExtension, - cliExecutable: Provider, - sentryOrg: String?, - buildEvents: BuildEventListenerRegistryInternal, -): Provider { - val sentryTelemetryProvider = SentryTelemetryService.register(project) - project.gradle.taskGraph.whenReady { - sentryTelemetryProvider.get().start { - SentryTelemetryService.createParameters( - project, - null, - extension, - cliExecutable, - sentryOrg, - "Android", - ) - } - buildEvents.onOperationCompletion(sentryTelemetryProvider) - } - return sentryTelemetryProvider -} From 612f5f87d83393a4c359942f8732ed2b2d00a35b Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 5 Mar 2026 17:28:05 +0100 Subject: [PATCH 11/12] fix(snapshots): Skip buildType paths when variant is null When variant is null (e.g. snapshot uploads), buildTypeName resolves to an empty string, producing nonsensical paths with double separators like src//sentry.properties. Guard buildTypeName-based paths the same way flavorName-based paths are already guarded. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../gradle/SentryPropertiesFileProvider.kt | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt index 88f04d623..e380900f5 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPropertiesFileProvider.kt @@ -28,12 +28,19 @@ internal object SentryPropertiesFileProvider { val sep = File.separator // Local Project dirs - val possibleFiles = mutableListOf("${projDir}${sep}src${sep}${buildTypeName}${sep}$FILENAME") + val possibleFiles = mutableListOf() + if (buildTypeName.isNotBlank()) { + possibleFiles.add("${projDir}${sep}src${sep}${buildTypeName}${sep}$FILENAME") + } if (flavorName.isNotBlank()) { - possibleFiles.add("${projDir}${sep}src${sep}${buildTypeName}${sep}$flavorName${sep}$FILENAME") - possibleFiles.add( - "${projDir}${sep}src${sep}${flavorName}${sep}${buildTypeName}${sep}$FILENAME" - ) + if (buildTypeName.isNotBlank()) { + possibleFiles.add( + "${projDir}${sep}src${sep}${buildTypeName}${sep}$flavorName${sep}$FILENAME" + ) + possibleFiles.add( + "${projDir}${sep}src${sep}${flavorName}${sep}${buildTypeName}${sep}$FILENAME" + ) + } possibleFiles.add("${projDir}${sep}src${sep}${flavorName}${sep}$FILENAME") } possibleFiles.add("${projDir}${sep}$FILENAME") @@ -45,15 +52,19 @@ internal object SentryPropertiesFileProvider { ) // Root project dirs - possibleFiles.add("${rootDir}${sep}src${sep}${buildTypeName}${sep}$FILENAME") + if (buildTypeName.isNotBlank()) { + possibleFiles.add("${rootDir}${sep}src${sep}${buildTypeName}${sep}$FILENAME") + } if (flavorName.isNotBlank()) { possibleFiles.add("${rootDir}${sep}src${sep}${flavorName}${sep}$FILENAME") - possibleFiles.add( - "${rootDir}${sep}src${sep}${buildTypeName}${sep}${flavorName}${sep}$FILENAME" - ) - possibleFiles.add( - "${rootDir}${sep}src${sep}${flavorName}${sep}${buildTypeName}${sep}$FILENAME" - ) + if (buildTypeName.isNotBlank()) { + possibleFiles.add( + "${rootDir}${sep}src${sep}${buildTypeName}${sep}${flavorName}${sep}$FILENAME" + ) + possibleFiles.add( + "${rootDir}${sep}src${sep}${flavorName}${sep}${buildTypeName}${sep}$FILENAME" + ) + } } possibleFiles.add("${rootDir}${sep}$FILENAME") From 2603da1e87ac846027985e2fcb7161be0f522577 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 5 Mar 2026 17:29:55 +0100 Subject: [PATCH 12/12] test(snapshots): Add tests for null variant property lookup Verify that getPropertiesFilePath with a null variant finds files in the project root and does not search buildType-specific paths. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../SentryPropertiesFileProviderTest.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/SentryPropertiesFileProviderTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/SentryPropertiesFileProviderTest.kt index 0d4639d30..39f0d533a 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/SentryPropertiesFileProviderTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/SentryPropertiesFileProviderTest.kt @@ -177,6 +177,28 @@ class SentryPropertiesFileProviderTest(private val agpVersion: SemVer) { assertEquals("42", File(getPropertiesFilePath(project, variant)!!).readText()) } + @Test + fun `getPropertiesFilePath with null variant finds file inside project folder`() { + val (project, _) = createTestAndroidProject(forceEvaluate = !AgpVersions.isAGP74(agpVersion)) + createTestFile(project.projectDir, "sentry.properties") + + assertEquals("42", File(getPropertiesFilePath(project)!!).readText()) + } + + @Test + fun `getPropertiesFilePath with null variant skips buildType paths`() { + val rootProject = ProjectBuilder.builder().build() + val (project, _) = + createTestAndroidProject( + parent = rootProject, + forceEvaluate = !AgpVersions.isAGP74(agpVersion), + ) + // Only place the file under src/debug/ — with null variant this should not be found + createTestFile(project.projectDir, "src${sep}debug${sep}sentry.properties") + + assertEquals(null, getPropertiesFilePath(project)) + } + @Test fun `getPropertiesFilePath finds file inside root buildType flavor folder`() { val rootProject = ProjectBuilder.builder().build()