Conversation
…shes, opt-in to reproducible bundle id
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Missing delimiter between path and content in hash
- The deterministic bundle hash now prefixes both path bytes and file bytes with their lengths before updating the digest, removing ambiguous concatenation collisions.
- ✅ Fixed: Platform-dependent newLine breaks cross-OS reproducibility
- The properties file writer now uses explicit "\n" line endings instead of platform-dependent
newLine()to keep output byte-identical across OSes.
- The properties file writer now uses explicit "\n" line endings instead of platform-dependent
Or push these changes by commenting:
@cursor push 13fbb069ed
Preview (13fbb069ed)
diff --git a/src/main/java/io/sentry/UploadSourceBundleMojo.java b/src/main/java/io/sentry/UploadSourceBundleMojo.java
--- a/src/main/java/io/sentry/UploadSourceBundleMojo.java
+++ b/src/main/java/io/sentry/UploadSourceBundleMojo.java
@@ -213,11 +213,11 @@
for (final @NotNull Path file : sortedFiles) {
final @NotNull String relativePath =
collectedSourcesDir.toPath().relativize(file).toString().replace('\\', '/');
- digest.update(relativePath.getBytes(StandardCharsets.UTF_8));
+ updateDigestWithLengthPrefix(digest, relativePath.getBytes(StandardCharsets.UTF_8));
// Include the file content in the hash
final byte[] fileBytes = Files.readAllBytes(file);
- digest.update(fileBytes);
+ updateDigestWithLengthPrefix(digest, fileBytes);
}
}
}
@@ -257,6 +257,12 @@
return new UUID(buffer.getLong(), buffer.getLong()).toString();
}
+ private static void updateDigestWithLengthPrefix(
+ final @NotNull MessageDigest digest, final byte[] data) {
+ digest.update(ByteBuffer.allocate(Integer.BYTES).putInt(data.length).array());
+ digest.update(data);
+ }
+
private void bundleSources(
final @NotNull SentryCliRunner cliRunner,
final @NotNull String bundleId,
@@ -375,11 +381,11 @@
// Write properties without timestamp comment for reproducible builds
// Properties are written in sorted order for consistency
fileWriter.write("# Generated by sentry-maven-plugin");
- fileWriter.newLine();
+ fileWriter.write("\n");
fileWriter.write("io.sentry.build-tool=maven");
- fileWriter.newLine();
+ fileWriter.write("\n");
fileWriter.write("io.sentry.bundle-ids=" + bundleId);
- fileWriter.newLine();
+ fileWriter.write("\n");
final @NotNull Resource resource = new Resource();
resource.setDirectory(sentryBuildDir.getPath());| @@ -1,2 +1,2 @@ | |||
| distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip | |||
| distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip | |||
There was a problem hiding this comment.
Is this a min version that our customers also need? If so, we should probably start mentioning it in changelog/readme/docs.
There was a problem hiding this comment.
Turns out it was actually the maven-jar-plugin that needs to be at version >= 3.2.0.
Maven 3.8.6 bundles version 2.4.0 of that plugin. I updated the changelog to reflect that this feature requires >= 3.2.0, updated the sample and reverted the maven version upgrade.
| bundleSources(cliRunner, bundleId, collectedSourcesTargetDir, sourceBundleTargetDir); | ||
| uploadSourceBundle(cliRunner, sourceBundleTargetDir); | ||
| } | ||
|
|
||
| private void collectSources(@NotNull String bundleId, @NotNull File outputDir) { | ||
| private void collectSources(@NotNull File outputDir) { |
There was a problem hiding this comment.
Bug: When reproducibleBundleId is true, the build silently falls back to a random UUID if generateDeterministicBundleId() fails, instead of failing the build.
Severity: MEDIUM
Suggested Fix
Modify generateDeterministicBundleId to throw an exception upon failure instead of returning null. This will cause the build to fail as expected when reproducibility cannot be guaranteed. Alternatively, introduce a new configuration option, similar to ignoreSourceBundleUploadFailure, to allow users to explicitly control the failure behavior.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: src/main/java/io/sentry/UploadSourceBundleMojo.java#L109-L113
Potential issue: When the `reproducibleBundleId` option is enabled, any exception during
the deterministic bundle ID generation (e.g., an `IOException` from file system issues)
is caught, and the `generateDeterministicBundleId` method returns `null`. This causes
the logic to fall back to generating a `UUID.randomUUID()`, silently producing a
non-reproducible build. This behavior violates the user's explicit configuration for
reproducibility, as they would expect the build to fail in such a scenario rather than
silently degrade.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Unused boolean constant added to PluginConfig
- Removed the unused DEFAULT_REPRODUCIBLE_BUNDLE_ID constant from PluginConfig while keeping the string default that is actually referenced.
Or push these changes by commenting:
@cursor push 04f3b28ab3
Preview (04f3b28ab3)
diff --git a/src/main/java/io/sentry/config/PluginConfig.java b/src/main/java/io/sentry/config/PluginConfig.java
--- a/src/main/java/io/sentry/config/PluginConfig.java
+++ b/src/main/java/io/sentry/config/PluginConfig.java
@@ -14,7 +14,6 @@
public static final @NotNull String DEFAULT_SKIP_SOURCE_BUNDLE_STRING = "false";
public static final boolean DEFAULT_IGNORE_SOURCE_BUNDLE_UPLOAD_FAILURE = false;
public static final @NotNull String DEFAULT_IGNORE_SOURCE_BUNDLE_UPLOAD_FAILURE_STRING = "false";
- public static final boolean DEFAULT_REPRODUCIBLE_BUNDLE_ID = false;
public static final @NotNull String DEFAULT_REPRODUCIBLE_BUNDLE_ID_STRING = "false";
public static final boolean DEFAULT_SKIP_TELEMETRY = false;
public static final @NotNull String DEFAULT_SKIP_TELEMETRY_STRING = "false";This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 814cc7b. Configure here.
| public static final @NotNull String DEFAULT_SKIP_SOURCE_BUNDLE_STRING = "false"; | ||
| public static final boolean DEFAULT_IGNORE_SOURCE_BUNDLE_UPLOAD_FAILURE = false; | ||
| public static final @NotNull String DEFAULT_IGNORE_SOURCE_BUNDLE_UPLOAD_FAILURE_STRING = "false"; | ||
| public static final boolean DEFAULT_REPRODUCIBLE_BUNDLE_ID = false; |
There was a problem hiding this comment.
Unused boolean constant added to PluginConfig
Low Severity
DEFAULT_REPRODUCIBLE_BUNDLE_ID (the boolean constant) is defined but never referenced anywhere in the codebase. Only DEFAULT_REPRODUCIBLE_BUNDLE_ID_STRING is used (in UploadSourceBundleMojo's @Parameter annotation). Unlike every other DEFAULT_* boolean constant in PluginConfig, there's no corresponding instance variable or usage for reproducibleBundleId in this class, making it dead code.
Reviewed by Cursor Bugbot for commit 814cc7b. Configure here.



📜 Description
Add the ability to calculate the bundleId based on Hashes of filepaths and file contents of the source context.
This allows for reproducible builds by having the bundleId stay the same if no files change.
This is an opt-in feature that can be enabled with:
💡 Motivation and Context
Resolves #213
💚 How did you test it?
📝 Checklist
🔮 Next steps