From 6960f25c9771372ff57c5e536f3eabdf3439198d Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Mon, 6 Oct 2025 11:15:29 +0200 Subject: [PATCH 1/4] Add support for reading distribution options from sentry-debug-meta.properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends DebugMetaPropertiesApplier to read and apply distribution configuration from properties files. This allows the Gradle plugin to populate distribution options (orgSlug, projectSlug, orgAuthToken, buildConfiguration) that will be automatically loaded when the SDK initializes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../util/DebugMetaPropertiesApplier.java | 65 +++++++++++ .../util/DebugMetaPropertiesApplierTest.kt | 102 ++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt diff --git a/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java b/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java index 236e9626826..3546be4fce3 100644 --- a/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java +++ b/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java @@ -17,6 +17,7 @@ public static void apply( if (debugMetaProperties != null) { applyToOptions(options, debugMetaProperties); applyBuildTool(options, debugMetaProperties); + applyDistributionOptions(options, debugMetaProperties); } } @@ -89,4 +90,68 @@ private static void applyBuildTool( final @NotNull Properties debugMetaProperties) { return debugMetaProperties.getProperty("io.sentry.build-tool-version"); } + + private static void applyDistributionOptions( + final @NotNull SentryOptions options, final @NotNull List debugMetaProperties) { + for (Properties properties : debugMetaProperties) { + final @Nullable String orgSlug = getDistributionOrgSlug(properties); + final @Nullable String projectSlug = getDistributionProjectSlug(properties); + final @Nullable String orgAuthToken = getDistributionOrgAuthToken(properties); + final @Nullable String buildConfiguration = getDistributionBuildConfiguration(properties); + + if (orgSlug != null || projectSlug != null || orgAuthToken != null) { + final @NotNull SentryOptions.DistributionOptions distributionOptions = + options.getDistribution(); + + if (orgSlug != null && distributionOptions.orgSlug.isEmpty()) { + options.getLogger().log(SentryLevel.DEBUG, "Distribution org slug found: %s", orgSlug); + distributionOptions.orgSlug = orgSlug; + } + + if (projectSlug != null && distributionOptions.projectSlug.isEmpty()) { + options + .getLogger() + .log(SentryLevel.DEBUG, "Distribution project slug found: %s", projectSlug); + distributionOptions.projectSlug = projectSlug; + } + + if (orgAuthToken != null && distributionOptions.orgAuthToken.isEmpty()) { + options.getLogger().log(SentryLevel.DEBUG, "Distribution org auth token found"); + distributionOptions.orgAuthToken = orgAuthToken; + } + + if (buildConfiguration != null && distributionOptions.buildConfiguration == null) { + options + .getLogger() + .log( + SentryLevel.DEBUG, + "Distribution build configuration found: %s", + buildConfiguration); + distributionOptions.buildConfiguration = buildConfiguration; + } + + break; + } + } + } + + private static @Nullable String getDistributionOrgSlug( + final @NotNull Properties debugMetaProperties) { + return debugMetaProperties.getProperty("io.sentry.distribution.org-slug"); + } + + private static @Nullable String getDistributionProjectSlug( + final @NotNull Properties debugMetaProperties) { + return debugMetaProperties.getProperty("io.sentry.distribution.project-slug"); + } + + private static @Nullable String getDistributionOrgAuthToken( + final @NotNull Properties debugMetaProperties) { + return debugMetaProperties.getProperty("io.sentry.distribution.org-auth-token"); + } + + private static @Nullable String getDistributionBuildConfiguration( + final @NotNull Properties debugMetaProperties) { + return debugMetaProperties.getProperty("io.sentry.distribution.build-configuration"); + } } diff --git a/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt new file mode 100644 index 00000000000..352c52b3b87 --- /dev/null +++ b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt @@ -0,0 +1,102 @@ +package io.sentry.util + +import io.sentry.SentryOptions +import java.util.Properties +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class DebugMetaPropertiesApplierTest { + + @Test + fun `applies distribution options from properties`() { + val properties = Properties() + properties.setProperty("io.sentry.distribution.org-slug", "test-org") + properties.setProperty("io.sentry.distribution.project-slug", "test-project") + properties.setProperty("io.sentry.distribution.org-auth-token", "test-token") + properties.setProperty("io.sentry.distribution.build-configuration", "debug") + + val options = SentryOptions() + DebugMetaPropertiesApplier.apply(options, listOf(properties)) + + assertEquals("test-org", options.distribution.orgSlug) + assertEquals("test-project", options.distribution.projectSlug) + assertEquals("test-token", options.distribution.orgAuthToken) + assertEquals("debug", options.distribution.buildConfiguration) + } + + @Test + fun `applies partial distribution options from properties`() { + val properties = Properties() + properties.setProperty("io.sentry.distribution.org-slug", "test-org") + properties.setProperty("io.sentry.distribution.project-slug", "test-project") + + val options = SentryOptions() + DebugMetaPropertiesApplier.apply(options, listOf(properties)) + + assertEquals("test-org", options.distribution.orgSlug) + assertEquals("test-project", options.distribution.projectSlug) + assertEquals("", options.distribution.orgAuthToken) + assertNull(options.distribution.buildConfiguration) + } + + @Test + fun `does not override existing distribution options`() { + val properties = Properties() + properties.setProperty("io.sentry.distribution.org-slug", "properties-org") + properties.setProperty("io.sentry.distribution.project-slug", "properties-project") + properties.setProperty("io.sentry.distribution.org-auth-token", "properties-token") + properties.setProperty("io.sentry.distribution.build-configuration", "properties-config") + + val options = SentryOptions() + options.distribution.orgSlug = "existing-org" + options.distribution.projectSlug = "existing-project" + options.distribution.orgAuthToken = "existing-token" + options.distribution.buildConfiguration = "existing-config" + + DebugMetaPropertiesApplier.apply(options, listOf(properties)) + + assertEquals("existing-org", options.distribution.orgSlug) + assertEquals("existing-project", options.distribution.projectSlug) + assertEquals("existing-token", options.distribution.orgAuthToken) + assertEquals("existing-config", options.distribution.buildConfiguration) + } + + @Test + fun `applies distribution options from first properties file with values`() { + val properties1 = Properties() + val properties2 = Properties() + properties2.setProperty("io.sentry.distribution.org-slug", "org-from-second") + properties2.setProperty("io.sentry.distribution.project-slug", "project-from-second") + + val options = SentryOptions() + DebugMetaPropertiesApplier.apply(options, listOf(properties1, properties2)) + + assertEquals("org-from-second", options.distribution.orgSlug) + assertEquals("project-from-second", options.distribution.projectSlug) + } + + @Test + fun `does nothing when properties list is empty`() { + val options = SentryOptions() + val originalOrgSlug = options.distribution.orgSlug + val originalProjectSlug = options.distribution.projectSlug + + DebugMetaPropertiesApplier.apply(options, emptyList()) + + assertEquals(originalOrgSlug, options.distribution.orgSlug) + assertEquals(originalProjectSlug, options.distribution.projectSlug) + } + + @Test + fun `does nothing when properties list is null`() { + val options = SentryOptions() + val originalOrgSlug = options.distribution.orgSlug + val originalProjectSlug = options.distribution.projectSlug + + DebugMetaPropertiesApplier.apply(options, null) + + assertEquals(originalOrgSlug, options.distribution.orgSlug) + assertEquals(originalProjectSlug, options.distribution.projectSlug) + } +} From 18dc3af5f4f13430ab48a09a8e4a3edcaa024505 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 7 Oct 2025 16:31:11 +0200 Subject: [PATCH 2/4] Address PR review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Include buildConfiguration in initial property check to fix bug where buildConfiguration-only properties would be skipped - Add isEmpty() checks for all property values before applying - Add comment explaining break statement (only process first properties file) - Add test for buildConfiguration-only scenario - Add test for empty string values 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../util/DebugMetaPropertiesApplier.java | 21 ++++++++++--- .../util/DebugMetaPropertiesApplierTest.kt | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java b/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java index 3546be4fce3..0782563c96f 100644 --- a/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java +++ b/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java @@ -99,28 +99,37 @@ private static void applyDistributionOptions( final @Nullable String orgAuthToken = getDistributionOrgAuthToken(properties); final @Nullable String buildConfiguration = getDistributionBuildConfiguration(properties); - if (orgSlug != null || projectSlug != null || orgAuthToken != null) { + if (orgSlug != null + || projectSlug != null + || orgAuthToken != null + || buildConfiguration != null) { final @NotNull SentryOptions.DistributionOptions distributionOptions = options.getDistribution(); - if (orgSlug != null && distributionOptions.orgSlug.isEmpty()) { + if (orgSlug != null && !orgSlug.isEmpty() && distributionOptions.orgSlug.isEmpty()) { options.getLogger().log(SentryLevel.DEBUG, "Distribution org slug found: %s", orgSlug); distributionOptions.orgSlug = orgSlug; } - if (projectSlug != null && distributionOptions.projectSlug.isEmpty()) { + if (projectSlug != null + && !projectSlug.isEmpty() + && distributionOptions.projectSlug.isEmpty()) { options .getLogger() .log(SentryLevel.DEBUG, "Distribution project slug found: %s", projectSlug); distributionOptions.projectSlug = projectSlug; } - if (orgAuthToken != null && distributionOptions.orgAuthToken.isEmpty()) { + if (orgAuthToken != null + && !orgAuthToken.isEmpty() + && distributionOptions.orgAuthToken.isEmpty()) { options.getLogger().log(SentryLevel.DEBUG, "Distribution org auth token found"); distributionOptions.orgAuthToken = orgAuthToken; } - if (buildConfiguration != null && distributionOptions.buildConfiguration == null) { + if (buildConfiguration != null + && !buildConfiguration.isEmpty() + && distributionOptions.buildConfiguration == null) { options .getLogger() .log( @@ -130,6 +139,8 @@ private static void applyDistributionOptions( distributionOptions.buildConfiguration = buildConfiguration; } + // We only process the first properties file that contains distribution options + // to maintain consistency with other properties like proguardUuid break; } } diff --git a/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt index 352c52b3b87..b5b30be9a72 100644 --- a/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt +++ b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt @@ -99,4 +99,35 @@ class DebugMetaPropertiesApplierTest { assertEquals(originalOrgSlug, options.distribution.orgSlug) assertEquals(originalProjectSlug, options.distribution.projectSlug) } + + @Test + fun `applies buildConfiguration only when it is the only property set`() { + val properties = Properties() + properties.setProperty("io.sentry.distribution.build-configuration", "debug") + + val options = SentryOptions() + DebugMetaPropertiesApplier.apply(options, listOf(properties)) + + assertEquals("debug", options.distribution.buildConfiguration) + assertEquals("", options.distribution.orgSlug) + assertEquals("", options.distribution.projectSlug) + assertEquals("", options.distribution.orgAuthToken) + } + + @Test + fun `does not apply empty string values`() { + val properties = Properties() + properties.setProperty("io.sentry.distribution.org-slug", "") + properties.setProperty("io.sentry.distribution.project-slug", "") + properties.setProperty("io.sentry.distribution.org-auth-token", "") + properties.setProperty("io.sentry.distribution.build-configuration", "") + + val options = SentryOptions() + DebugMetaPropertiesApplier.apply(options, listOf(properties)) + + assertEquals("", options.distribution.orgSlug) + assertEquals("", options.distribution.projectSlug) + assertEquals("", options.distribution.orgAuthToken) + assertNull(options.distribution.buildConfiguration) + } } From f6701a6a3c78cc7c3a0c35695339a9b6ca529114 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 8 Oct 2025 17:02:25 +0200 Subject: [PATCH 3/4] Update auth token property name and improve test coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename property from `io.sentry.distribution.org-auth-token` to `io.sentry.distribution.auth-token` to match sentry-android-gradle-plugin - Rename method from `getDistributionOrgAuthToken` to `getDistributionAuthToken` - Improve test to verify first properties file with distribution options is used 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../util/DebugMetaPropertiesApplier.java | 6 +++--- .../util/DebugMetaPropertiesApplierTest.kt | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java b/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java index 0782563c96f..a6ff901b907 100644 --- a/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java +++ b/sentry/src/main/java/io/sentry/util/DebugMetaPropertiesApplier.java @@ -96,7 +96,7 @@ private static void applyDistributionOptions( for (Properties properties : debugMetaProperties) { final @Nullable String orgSlug = getDistributionOrgSlug(properties); final @Nullable String projectSlug = getDistributionProjectSlug(properties); - final @Nullable String orgAuthToken = getDistributionOrgAuthToken(properties); + final @Nullable String orgAuthToken = getDistributionAuthToken(properties); final @Nullable String buildConfiguration = getDistributionBuildConfiguration(properties); if (orgSlug != null @@ -156,9 +156,9 @@ private static void applyDistributionOptions( return debugMetaProperties.getProperty("io.sentry.distribution.project-slug"); } - private static @Nullable String getDistributionOrgAuthToken( + private static @Nullable String getDistributionAuthToken( final @NotNull Properties debugMetaProperties) { - return debugMetaProperties.getProperty("io.sentry.distribution.org-auth-token"); + return debugMetaProperties.getProperty("io.sentry.distribution.auth-token"); } private static @Nullable String getDistributionBuildConfiguration( diff --git a/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt index b5b30be9a72..723903c0003 100644 --- a/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt +++ b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt @@ -13,7 +13,7 @@ class DebugMetaPropertiesApplierTest { val properties = Properties() properties.setProperty("io.sentry.distribution.org-slug", "test-org") properties.setProperty("io.sentry.distribution.project-slug", "test-project") - properties.setProperty("io.sentry.distribution.org-auth-token", "test-token") + properties.setProperty("io.sentry.distribution.auth-token", "test-token") properties.setProperty("io.sentry.distribution.build-configuration", "debug") val options = SentryOptions() @@ -45,7 +45,7 @@ class DebugMetaPropertiesApplierTest { val properties = Properties() properties.setProperty("io.sentry.distribution.org-slug", "properties-org") properties.setProperty("io.sentry.distribution.project-slug", "properties-project") - properties.setProperty("io.sentry.distribution.org-auth-token", "properties-token") + properties.setProperty("io.sentry.distribution.auth-token", "properties-token") properties.setProperty("io.sentry.distribution.build-configuration", "properties-config") val options = SentryOptions() @@ -66,13 +66,20 @@ class DebugMetaPropertiesApplierTest { fun `applies distribution options from first properties file with values`() { val properties1 = Properties() val properties2 = Properties() - properties2.setProperty("io.sentry.distribution.org-slug", "org-from-second") + val properties3 = Properties() + + // properties1 has non-distribution properties so is ignored: + properties1.setProperty("io.sentry.unrelated", "unrelated") + + // properties2 should end up being the ones set properties2.setProperty("io.sentry.distribution.project-slug", "project-from-second") + // properties3 also has distribution properties but since properties2 was first they are ignored. + properties3.setProperty("io.sentry.distribution.project-slug", "project-from-third") + val options = SentryOptions() - DebugMetaPropertiesApplier.apply(options, listOf(properties1, properties2)) + DebugMetaPropertiesApplier.apply(options, listOf(properties1, properties2, properties3)) - assertEquals("org-from-second", options.distribution.orgSlug) assertEquals("project-from-second", options.distribution.projectSlug) } @@ -119,7 +126,7 @@ class DebugMetaPropertiesApplierTest { val properties = Properties() properties.setProperty("io.sentry.distribution.org-slug", "") properties.setProperty("io.sentry.distribution.project-slug", "") - properties.setProperty("io.sentry.distribution.org-auth-token", "") + properties.setProperty("io.sentry.distribution.auth-token", "") properties.setProperty("io.sentry.distribution.build-configuration", "") val options = SentryOptions() From 7d2b6b441dd99a2a88157dc9518f9217a772f858 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 8 Oct 2025 15:06:19 +0000 Subject: [PATCH 4/4] Format code --- .../test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt index 723903c0003..63c8a62c913 100644 --- a/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt +++ b/sentry/src/test/java/io/sentry/util/DebugMetaPropertiesApplierTest.kt @@ -74,7 +74,8 @@ class DebugMetaPropertiesApplierTest { // properties2 should end up being the ones set properties2.setProperty("io.sentry.distribution.project-slug", "project-from-second") - // properties3 also has distribution properties but since properties2 was first they are ignored. + // properties3 also has distribution properties but since properties2 was first they are + // ignored. properties3.setProperty("io.sentry.distribution.project-slug", "project-from-third") val options = SentryOptions()