sentry-cli 3.4.0__tar.gz → 3.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/Cargo.lock +1 -1
  2. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/Cargo.toml +1 -1
  3. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/PKG-INFO +1 -1
  4. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/sentry_cli.egg-info/PKG-INFO +1 -1
  5. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/snapshots.rs +2 -2
  6. sentry_cli-3.4.1/src/commands/debug_files/bundle_jvm.rs +535 -0
  7. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/file_search.rs +14 -0
  8. sentry_cli-3.4.0/src/commands/debug_files/bundle_jvm.rs +0 -250
  9. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/LICENSE +0 -0
  10. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/MANIFEST.in +0 -0
  11. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/README.md +0 -0
  12. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/AGENTS.md +0 -0
  13. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/CLAUDE.md +0 -0
  14. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/Cargo.toml +0 -0
  15. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/build.rs +0 -0
  16. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/native/swift/AssetCatalogParser/Package.swift +0 -0
  17. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/native/swift/AssetCatalogParser/Sources/AssetCatalogParser/AssetCatalogReader.swift +0 -0
  18. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/native/swift/AssetCatalogParser/Sources/ObjcSupport/include/safeValueForKey.h +0 -0
  19. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/native/swift/AssetCatalogParser/Sources/ObjcSupport/safeValueForKey.m +0 -0
  20. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/native/swift/AssetCatalogParser/Tests/AssetCatalogParserTests/AssetCatalogParserTests.swift +0 -0
  21. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/native/swift/AssetCatalogParser/Tests/AssetCatalogParserTests/Resources/test.xcarchive/Info.plist +0 -0
  22. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/native/swift/AssetCatalogParser/Tests/AssetCatalogParserTests/Resources/test.xcarchive/Products/Applications/DemoApp.app/Assets.car +0 -0
  23. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/src/asset_catalog.rs +0 -0
  24. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/apple-catalog-parsing/src/lib.rs +0 -0
  25. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/build.rs +0 -0
  26. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/pyproject.toml +0 -0
  27. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/sentry_cli.egg-info/SOURCES.txt +0 -0
  28. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/sentry_cli.egg-info/dependency_links.txt +0 -0
  29. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/sentry_cli.egg-info/top_level.txt +0 -0
  30. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/setup.cfg +0 -0
  31. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/setup.py +0 -0
  32. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/AGENTS.md +0 -0
  33. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/CLAUDE.md +0 -0
  34. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/connection_manager.rs +0 -0
  35. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/artifact.rs +0 -0
  36. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/build.rs +0 -0
  37. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/compression.rs +0 -0
  38. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/dif.rs +0 -0
  39. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/file_state.rs +0 -0
  40. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/hash_algorithm.rs +0 -0
  41. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/mod.rs +0 -0
  42. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/upload/mod.rs +0 -0
  43. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/chunking/upload/options.rs +0 -0
  44. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/code_mappings.rs +0 -0
  45. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/deploy.rs +0 -0
  46. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/data_types/mod.rs +0 -0
  47. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/encoding.rs +0 -0
  48. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/envelopes_api.rs +0 -0
  49. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/errors/api_error.rs +0 -0
  50. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/errors/mod.rs +0 -0
  51. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/errors/sentry_error.rs +0 -0
  52. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/mod.rs +0 -0
  53. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/pagination.rs +0 -0
  54. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/api/serialization.rs +0 -0
  55. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/bashsupport.sh +0 -0
  56. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/bash_hook.rs +0 -0
  57. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/build/download.rs +0 -0
  58. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/build/mod.rs +0 -0
  59. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/build/snapshots.rs +0 -0
  60. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/build/upload.rs +0 -0
  61. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/code_mappings/mod.rs +0 -0
  62. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/code_mappings/upload.rs +0 -0
  63. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/dart_symbol_map/mod.rs +0 -0
  64. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/dart_symbol_map/upload.rs +0 -0
  65. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/debug_files/bundle_sources.rs +0 -0
  66. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/debug_files/check.rs +0 -0
  67. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/debug_files/find.rs +0 -0
  68. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/debug_files/mod.rs +0 -0
  69. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/debug_files/print_sources.rs +0 -0
  70. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/debug_files/upload.rs +0 -0
  71. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/deploys/list.rs +0 -0
  72. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/deploys/mod.rs +0 -0
  73. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/deploys/new.rs +0 -0
  74. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/derive_parser.rs +0 -0
  75. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/events/list.rs +0 -0
  76. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/events/mod.rs +0 -0
  77. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/info.rs +0 -0
  78. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/issues/list.rs +0 -0
  79. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/issues/mod.rs +0 -0
  80. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/issues/mute.rs +0 -0
  81. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/issues/resolve.rs +0 -0
  82. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/issues/unresolve.rs +0 -0
  83. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/login.rs +0 -0
  84. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/logs/list.rs +0 -0
  85. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/logs/mod.rs +0 -0
  86. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/mod.rs +0 -0
  87. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/monitors/list.rs +0 -0
  88. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/monitors/mod.rs +0 -0
  89. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/monitors/run.rs +0 -0
  90. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/organizations/list.rs +0 -0
  91. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/organizations/mod.rs +0 -0
  92. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/proguard/mod.rs +0 -0
  93. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/proguard/upload.rs +0 -0
  94. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/proguard/uuid.rs +0 -0
  95. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/projects/list.rs +0 -0
  96. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/projects/mod.rs +0 -0
  97. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/react_native/gradle.rs +0 -0
  98. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/react_native/mod.rs +0 -0
  99. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/react_native/xcode.rs +0 -0
  100. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/archive.rs +0 -0
  101. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/delete.rs +0 -0
  102. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/finalize.rs +0 -0
  103. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/info.rs +0 -0
  104. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/list.rs +0 -0
  105. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/mod.rs +0 -0
  106. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/new.rs +0 -0
  107. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/propose_version.rs +0 -0
  108. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/restore.rs +0 -0
  109. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/releases/set_commits.rs +0 -0
  110. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/repos/list.rs +0 -0
  111. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/repos/mod.rs +0 -0
  112. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/send_envelope.rs +0 -0
  113. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/send_event.rs +0 -0
  114. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/send_metric/common_args.rs +0 -0
  115. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/send_metric/increment.rs +0 -0
  116. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/send_metric/mod.rs +0 -0
  117. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/send_metric/set.rs +0 -0
  118. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/sourcemaps/inject.rs +0 -0
  119. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/sourcemaps/mod.rs +0 -0
  120. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/sourcemaps/resolve.rs +0 -0
  121. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/sourcemaps/upload.rs +0 -0
  122. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/uninstall.rs +0 -0
  123. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/update.rs +0 -0
  124. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/upload_dif.rs +0 -0
  125. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/upload_dsym.rs +0 -0
  126. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/commands/upload_proguard.rs +0 -0
  127. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/config.rs +0 -0
  128. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/constants.rs +0 -0
  129. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/main.rs +0 -0
  130. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/android.rs +0 -0
  131. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/args.rs +0 -0
  132. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/auth_token/auth_token_impl.rs +0 -0
  133. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/auth_token/error.rs +0 -0
  134. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/auth_token/mod.rs +0 -0
  135. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/auth_token/org_auth_token.rs +0 -0
  136. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/auth_token/redacting.rs +0 -0
  137. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/auth_token/test.rs +0 -0
  138. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/auth_token/user_auth_token.rs +0 -0
  139. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/build/apple.rs +0 -0
  140. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/build/mod.rs +0 -0
  141. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/build/normalize.rs +0 -0
  142. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/build/validation.rs +0 -0
  143. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/build_vcs.rs +0 -0
  144. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/chunks/mod.rs +0 -0
  145. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/chunks/options.rs +0 -0
  146. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/chunks/types.rs +0 -0
  147. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/chunks/upload.rs +0 -0
  148. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/ci.rs +0 -0
  149. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/cordova.rs +0 -0
  150. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/dif.rs +0 -0
  151. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/dif_upload/error.rs +0 -0
  152. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/dif_upload/mod.rs +0 -0
  153. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/event.rs +0 -0
  154. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/file_upload.rs +0 -0
  155. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/formatting.rs +0 -0
  156. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/fs.rs +0 -0
  157. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/http.rs +0 -0
  158. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/logging.rs +0 -0
  159. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/mod.rs +0 -0
  160. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/non_empty.rs +0 -0
  161. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/progress.rs +0 -0
  162. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/proguard/mapping.rs +0 -0
  163. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/proguard/mod.rs +0 -0
  164. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/proguard/upload.rs +0 -0
  165. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/releases.rs +0 -0
  166. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/retry.rs +0 -0
  167. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_default_twenty.snap +0 -0
  168. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_ignore_missing.snap +0 -0
  169. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_set_base.snap +0 -0
  170. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_set_previous_commit.snap +0 -0
  171. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/snapshots/sentry_cli__utils__vcs__tests__get_commits_from_git.snap +0 -0
  172. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/source_bundle.rs +0 -0
  173. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/sourcemaps/inject.rs +0 -0
  174. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/sourcemaps.rs +0 -0
  175. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/system.rs +0 -0
  176. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/ui.rs +0 -0
  177. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/update.rs +0 -0
  178. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/value_parsers.rs +0 -0
  179. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/vcs.rs +0 -0
  180. {sentry_cli-3.4.0 → sentry_cli-3.4.1}/src/utils/xcode.rs +0 -0
@@ -3392,7 +3392,7 @@ dependencies = [
3392
3392
 
3393
3393
  [[package]]
3394
3394
  name = "sentry-cli"
3395
- version = "3.4.0"
3395
+ version = "3.4.1"
3396
3396
  dependencies = [
3397
3397
  "anyhow",
3398
3398
  "anylog",
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  build = "build.rs"
3
3
  name = "sentry-cli"
4
- version = "3.4.0"
4
+ version = "3.4.1"
5
5
  edition = "2021"
6
6
  rust-version = "1.91"
7
7
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sentry_cli
3
- Version: 3.4.0
3
+ Version: 3.4.1
4
4
  Summary: A command line utility to work with Sentry.
5
5
  Home-page: https://github.com/getsentry/sentry-cli
6
6
  Author: Sentry
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sentry_cli
3
- Version: 3.4.0
3
+ Version: 3.4.1
4
4
  Summary: A command line utility to work with Sentry.
5
5
  Home-page: https://github.com/getsentry/sentry-cli
6
6
  Author: Sentry
@@ -40,8 +40,8 @@ pub struct SnapshotsManifest<'a> {
40
40
  // Keep in sync with https://github.com/getsentry/sentry/blob/master/src/sentry/preprod/snapshots/manifest.py
41
41
  /// Metadata for a single image in a snapshot manifest.
42
42
  ///
43
- /// CLI-managed fields (`image_file_name`, `width`, `height`) override any
44
- /// identically named fields provided by user sidecar metadata.
43
+ /// CLI-managed fields (`width`, `height`) override any identically named
44
+ /// fields provided by user sidecar metadata.
45
45
  #[derive(Debug, Serialize)]
46
46
  pub struct ImageMetadata {
47
47
  #[serde(flatten)]
@@ -0,0 +1,535 @@
1
+ #![expect(clippy::unwrap_used, reason = "contains legacy code which uses unwrap")]
2
+
3
+ use crate::config::Config;
4
+ use crate::utils::args::ArgExt as _;
5
+ use crate::utils::file_search::{ReleaseFileMatch, ReleaseFileSearch};
6
+ use crate::utils::file_upload::SourceFile;
7
+ use crate::utils::fs::path_as_url;
8
+ use crate::utils::source_bundle::{self, BundleContext};
9
+ use anyhow::{bail, Context as _, Result};
10
+ use clap::{Arg, ArgAction, ArgMatches, Command};
11
+ use log::{debug, warn};
12
+ use regex::Regex;
13
+ use sentry::types::DebugId;
14
+ use std::collections::hash_map::Entry;
15
+ use std::collections::{BTreeMap, HashMap};
16
+ use std::ffi::OsStr;
17
+ use std::fs;
18
+ use std::path::{Path, PathBuf};
19
+ use std::str::FromStr as _;
20
+ use std::sync::{Arc, LazyLock};
21
+ use symbolic::debuginfo::sourcebundle::SourceFileType;
22
+
23
+ const JVM_EXTENSIONS: &[&str] = &[
24
+ "java", "kt", "scala", "sc", "groovy", "gvy", "gy", "gsh", "clj", "cljc",
25
+ ];
26
+
27
+ /// Directory names that mark the root of a JVM source set (i.e. the parent of
28
+ /// the package hierarchy). Matches the Gradle/Maven convention
29
+ /// `src/<sourceset>/<lang>/<package>/...`.
30
+ const SOURCE_SET_LANGS: &[&str] = &["java", "kotlin", "scala", "groovy", "clojure"];
31
+
32
+ static SOURCE_SET_PREFIX_RE: LazyLock<Regex> = LazyLock::new(|| {
33
+ let langs = SOURCE_SET_LANGS.join("|");
34
+ Regex::new(&format!(
35
+ r"(?:^|[/\\])src[/\\][^/\\]+[/\\](?:{langs})[/\\](.+)$"
36
+ ))
37
+ .expect("valid regex")
38
+ });
39
+
40
+ /// Strips the `[<module>/]src/<sourceset>/<lang>/` prefix from a relative source
41
+ /// path so the remaining portion matches what Symbolicator looks up by URL
42
+ /// (e.g. `io/sentry/android/core/ANRWatchDog.java`). This is needed because
43
+ /// JVM stack traces reference classes by their package path, with no knowledge
44
+ /// of the containing Gradle module or source-set layout on disk.
45
+ ///
46
+ /// Returns the path unchanged if no `src/<sourceset>/<lang>/` segment is found.
47
+ fn strip_source_set_prefix(relative_path: &Path) -> PathBuf {
48
+ relative_path
49
+ .to_str()
50
+ .and_then(|s| SOURCE_SET_PREFIX_RE.captures(s))
51
+ .map(|caps| PathBuf::from(&caps[1]))
52
+ .unwrap_or_else(|| relative_path.to_path_buf())
53
+ }
54
+
55
+ /// Builds the Symbolicator-compatible URL for a relative source path
56
+ /// (e.g. `~/io/sentry/android/core/ANRWatchDog.jvm`).
57
+ fn build_source_url(relative_path: &Path) -> String {
58
+ let package_path = strip_source_set_prefix(relative_path);
59
+ let package_path_jvm_ext = package_path.with_extension("jvm");
60
+ format!("~/{}", path_as_url(&package_path_jvm_ext))
61
+ }
62
+
63
+ /// Turns walked source files into `SourceFile`s for bundling, filtering out
64
+ /// build-output directories and deduplicating by URL.
65
+ ///
66
+ /// Android build variants can contribute the same FQCN from different source
67
+ /// sets (e.g. `src/main/` and `src/debug/` both defining `com.example.Foo`).
68
+ /// After stripping, both map to the same URL — this keeps the first-seen
69
+ /// entry and warns the user about the rest.
70
+ fn build_source_files(sources: Vec<ReleaseFileMatch>) -> Vec<SourceFile> {
71
+ let candidates = sources.into_iter().filter_map(|source| {
72
+ let local_path = source.path.strip_prefix(&source.base_path).unwrap();
73
+ if is_in_ambiguous_build_dir(local_path) {
74
+ debug!("excluding (build output): {}", source.path.display());
75
+ return None;
76
+ }
77
+ let url = build_source_url(local_path);
78
+ Some((url, source))
79
+ });
80
+
81
+ let mut seen_urls: HashMap<String, usize> = HashMap::new();
82
+ let mut files: Vec<SourceFile> = Vec::new();
83
+
84
+ for (url, source) in candidates {
85
+ match seen_urls.entry(url) {
86
+ Entry::Occupied(existing) => {
87
+ warn!(
88
+ "URL collision on {}: skipping '{}' (already bundled from '{}'). \
89
+ Use --exclude to drop the unwanted source set \
90
+ (e.g. --exclude='**/src/debug/**').",
91
+ existing.key(),
92
+ source.path.display(),
93
+ files[*existing.get()].path.display(),
94
+ );
95
+ }
96
+ Entry::Vacant(slot) => {
97
+ let url = slot.key().clone();
98
+ slot.insert(files.len());
99
+ files.push(SourceFile {
100
+ url,
101
+ path: source.path,
102
+ contents: Arc::new(source.contents),
103
+ ty: SourceFileType::Source,
104
+ headers: BTreeMap::new(),
105
+ messages: vec![],
106
+ already_uploaded: false,
107
+ });
108
+ }
109
+ }
110
+ }
111
+ files
112
+ }
113
+
114
+ /// Safe to exclude globally — can never be valid JVM package names.
115
+ const SAFE_EXCLUDES: &[&str] = &[
116
+ ".cxx",
117
+ ".eclipse",
118
+ ".fleet",
119
+ ".gradle",
120
+ ".idea",
121
+ ".kotlin",
122
+ ".mvn",
123
+ ".settings",
124
+ ".vscode",
125
+ "node_modules",
126
+ ];
127
+
128
+ /// Common build output dirs that could also be valid JVM package names
129
+ /// (e.g. `com.example.build`). Only excluded outside of `src/` directories.
130
+ const AMBIGUOUS_EXCLUDES: &[&str] = &["bin", "build", "out", "target"];
131
+
132
+ /// Checks *all* ambiguous directories in the path and excludes if any of them
133
+ /// is not under a `src/` ancestor. Handles nested cases like
134
+ /// `build/src/main/java/com/example/target/Foo.java` — inner `target` is under
135
+ /// `src`, but outer `build` is not, so the file is excluded.
136
+ fn is_in_ambiguous_build_dir(relative_path: &Path) -> bool {
137
+ for ancestor in relative_path.ancestors() {
138
+ let Some(name) = ancestor.file_name().and_then(|n| n.to_str()) else {
139
+ continue;
140
+ };
141
+ if AMBIGUOUS_EXCLUDES.contains(&name) {
142
+ // Check if any ancestor *above* this directory is named "src".
143
+ let has_src_above = ancestor
144
+ .ancestors()
145
+ .skip(1) // skip the ambiguous dir itself
146
+ .any(|a| a.file_name() == Some(OsStr::new("src")));
147
+ if !has_src_above {
148
+ return true;
149
+ }
150
+ }
151
+ }
152
+ false
153
+ }
154
+
155
+ pub fn make_command(command: Command) -> Command {
156
+ command
157
+ .hide(true) // experimental for now
158
+ .about(
159
+ "Create a source bundle for the given JVM based source files (e.g. Java, Kotlin, ...)",
160
+ )
161
+ .org_arg()
162
+ .project_arg(false)
163
+ .arg(
164
+ Arg::new("path")
165
+ .value_name("PATH")
166
+ .required(true)
167
+ .value_parser(clap::builder::PathBufValueParser::new())
168
+ .help("The directory containing source files to bundle."),
169
+ )
170
+ .arg(
171
+ Arg::new("output")
172
+ .long("output")
173
+ .value_name("PATH")
174
+ .required(true)
175
+ .value_parser(clap::builder::PathBufValueParser::new())
176
+ .help("The path to the output folder."),
177
+ )
178
+ .arg(
179
+ Arg::new("debug_id")
180
+ .long("debug-id")
181
+ .value_name("UUID")
182
+ .required(true)
183
+ .value_parser(DebugId::from_str)
184
+ .help("Debug ID (UUID) to use for the source bundle."),
185
+ )
186
+ .arg(
187
+ Arg::new("exclude")
188
+ .long("exclude")
189
+ .value_name("PATTERN")
190
+ .action(ArgAction::Append)
191
+ .help(
192
+ "Glob pattern to exclude files/directories. Can be repeated. \
193
+ By default, common build output and IDE directories are excluded \
194
+ (build, .gradle, target, .idea, .vscode, out, bin, etc.).",
195
+ ),
196
+ )
197
+ }
198
+
199
+ pub fn execute(matches: &ArgMatches) -> Result<()> {
200
+ let config = Config::current();
201
+ let org = config.get_org(matches)?;
202
+ let project = config.get_project(matches).ok();
203
+
204
+ let context = BundleContext::new(&org).with_projects(project.as_slice());
205
+ let path = matches.get_one::<PathBuf>("path").unwrap();
206
+ let output_path = matches.get_one::<PathBuf>("output").unwrap();
207
+ let debug_id = matches.get_one::<DebugId>("debug_id").unwrap();
208
+ let out = output_path.join(format!("{debug_id}.zip"));
209
+
210
+ if !path.exists() {
211
+ bail!("Given path does not exist: {}", path.display())
212
+ }
213
+
214
+ if !path.is_dir() {
215
+ bail!("Given path is not a directory: {}", path.display())
216
+ }
217
+
218
+ if !output_path.exists() {
219
+ fs::create_dir_all(output_path).context(format!(
220
+ "Failed to create output directory {}",
221
+ output_path.display()
222
+ ))?;
223
+ }
224
+
225
+ let all_excludes = SAFE_EXCLUDES
226
+ .iter()
227
+ .copied()
228
+ .chain(
229
+ matches
230
+ .get_many::<String>("exclude")
231
+ .into_iter()
232
+ .flatten()
233
+ .map(|s| s.as_str()),
234
+ )
235
+ .map(|v| format!("!{v}"));
236
+
237
+ let sources = ReleaseFileSearch::new(path.clone())
238
+ .extensions(JVM_EXTENSIONS.iter().copied())
239
+ .ignores(all_excludes)
240
+ .respect_ignores(true)
241
+ .sort_entries(true)
242
+ .collect_files()?;
243
+
244
+ let files = build_source_files(sources);
245
+
246
+ let tempfile = source_bundle::build(context, files, Some(*debug_id))
247
+ .context("Unable to create source bundle")?;
248
+
249
+ fs::copy(tempfile.path(), &out).context("Unable to write source bundle")?;
250
+ println!("Created {}", out.display());
251
+
252
+ Ok(())
253
+ }
254
+
255
+ #[cfg(test)]
256
+ mod log_capture {
257
+ use log::{Level, LevelFilter, Log, Metadata, Record};
258
+ use std::cell::RefCell;
259
+ use std::sync::Once;
260
+
261
+ thread_local! {
262
+ static BUFFER: RefCell<Vec<(Level, String)>> = const { RefCell::new(Vec::new()) };
263
+ }
264
+
265
+ struct CaptureLogger;
266
+
267
+ impl Log for CaptureLogger {
268
+ fn enabled(&self, _: &Metadata) -> bool {
269
+ true
270
+ }
271
+
272
+ fn log(&self, record: &Record) {
273
+ BUFFER.with(|b| {
274
+ b.borrow_mut()
275
+ .push((record.level(), record.args().to_string()))
276
+ });
277
+ }
278
+
279
+ fn flush(&self) {}
280
+ }
281
+
282
+ static LOGGER: CaptureLogger = CaptureLogger;
283
+
284
+ /// Installs the capture logger (once per process) and clears this
285
+ /// thread's buffer so a test starts from a clean slate.
286
+ pub fn setup() {
287
+ static ONCE: Once = Once::new();
288
+ ONCE.call_once(|| {
289
+ let _ = log::set_logger(&LOGGER);
290
+ log::set_max_level(LevelFilter::Trace);
291
+ });
292
+ BUFFER.with(|b| b.borrow_mut().clear());
293
+ }
294
+
295
+ pub fn warnings() -> Vec<String> {
296
+ BUFFER.with(|b| {
297
+ b.borrow()
298
+ .iter()
299
+ .filter(|(lvl, _)| *lvl == Level::Warn)
300
+ .map(|(_, msg)| msg.clone())
301
+ .collect()
302
+ })
303
+ }
304
+ }
305
+
306
+ #[cfg(test)]
307
+ mod tests {
308
+ use super::*;
309
+ use std::path::Path;
310
+
311
+ #[test]
312
+ fn test_excludes_build_output_at_module_root() {
313
+ assert!(is_in_ambiguous_build_dir(Path::new(
314
+ "app/build/generated/Foo.java"
315
+ )));
316
+ assert!(is_in_ambiguous_build_dir(Path::new(
317
+ "build/generated/Foo.java"
318
+ )));
319
+ assert!(is_in_ambiguous_build_dir(Path::new(
320
+ "module/target/classes/Foo.java"
321
+ )));
322
+ assert!(is_in_ambiguous_build_dir(Path::new("bin/Foo.class")));
323
+ assert!(is_in_ambiguous_build_dir(Path::new(
324
+ "out/production/Foo.java"
325
+ )));
326
+ }
327
+
328
+ #[test]
329
+ fn test_keeps_source_packages_under_src() {
330
+ assert!(!is_in_ambiguous_build_dir(Path::new(
331
+ "src/main/java/com/example/build/Builder.java"
332
+ )));
333
+ assert!(!is_in_ambiguous_build_dir(Path::new(
334
+ "app/src/main/java/com/example/target/Target.java"
335
+ )));
336
+ assert!(!is_in_ambiguous_build_dir(Path::new(
337
+ "src/main/kotlin/com/example/out/Output.kt"
338
+ )));
339
+ }
340
+
341
+ #[test]
342
+ fn test_excludes_build_dir_containing_src() {
343
+ // build/src/... should still be excluded — src is *inside* build, not above it
344
+ assert!(is_in_ambiguous_build_dir(Path::new(
345
+ "build/src/main/java/Foo.java"
346
+ )));
347
+ assert!(is_in_ambiguous_build_dir(Path::new(
348
+ "app/build/src/generated/Foo.java"
349
+ )));
350
+ }
351
+
352
+ #[test]
353
+ fn test_excludes_nested_ambiguous_dirs_under_build() {
354
+ // build/src/.../target/ — inner `target` is under src, but outer `build` is not
355
+ assert!(is_in_ambiguous_build_dir(Path::new(
356
+ "build/src/main/java/com/example/target/Foo.java"
357
+ )));
358
+ assert!(is_in_ambiguous_build_dir(Path::new(
359
+ "target/src/main/java/com/example/out/Foo.java"
360
+ )));
361
+ }
362
+
363
+ #[test]
364
+ fn test_strip_source_set_prefix_drops_module_and_source_set() {
365
+ assert_eq!(
366
+ strip_source_set_prefix(Path::new(
367
+ "sentry-android-core/src/main/java/io/sentry/android/core/ANRWatchDog.java"
368
+ )),
369
+ Path::new("io/sentry/android/core/ANRWatchDog.java")
370
+ );
371
+ assert_eq!(
372
+ strip_source_set_prefix(Path::new("src/main/kotlin/com/example/Foo.kt")),
373
+ Path::new("com/example/Foo.kt")
374
+ );
375
+ }
376
+
377
+ #[test]
378
+ fn test_strip_source_set_prefix_kt_under_java_source_set() {
379
+ // Mixed Java/Kotlin projects commonly place .kt files under src/main/java/
380
+ // — stripping is driven by the directory name, not the file extension.
381
+ assert_eq!(
382
+ strip_source_set_prefix(Path::new("src/main/java/com/example/Foo.kt")),
383
+ Path::new("com/example/Foo.kt")
384
+ );
385
+ assert_eq!(
386
+ strip_source_set_prefix(Path::new(
387
+ "app/src/main/java/io/sentry/android/core/ANRWatchDog.kt"
388
+ )),
389
+ Path::new("io/sentry/android/core/ANRWatchDog.kt")
390
+ );
391
+ }
392
+
393
+ #[test]
394
+ fn test_strip_source_set_prefix_handles_nested_modules() {
395
+ assert_eq!(
396
+ strip_source_set_prefix(Path::new(
397
+ "sentry-opentelemetry/sentry-opentelemetry-agent/src/main/java/io/sentry/opentelemetry/agent/Foo.java"
398
+ )),
399
+ Path::new("io/sentry/opentelemetry/agent/Foo.java")
400
+ );
401
+ }
402
+
403
+ #[test]
404
+ fn test_strip_source_set_prefix_handles_android_variants() {
405
+ assert_eq!(
406
+ strip_source_set_prefix(Path::new("app/src/debug/java/com/example/Foo.java")),
407
+ Path::new("com/example/Foo.java")
408
+ );
409
+ assert_eq!(
410
+ strip_source_set_prefix(Path::new("lib/src/release/kotlin/com/example/Bar.kt")),
411
+ Path::new("com/example/Bar.kt")
412
+ );
413
+ }
414
+
415
+ #[test]
416
+ fn test_strip_source_set_prefix_supports_scala_and_groovy() {
417
+ assert_eq!(
418
+ strip_source_set_prefix(Path::new("mod/src/main/scala/com/example/Foo.scala")),
419
+ Path::new("com/example/Foo.scala")
420
+ );
421
+ assert_eq!(
422
+ strip_source_set_prefix(Path::new("mod/src/main/groovy/com/example/Foo.groovy")),
423
+ Path::new("com/example/Foo.groovy")
424
+ );
425
+ }
426
+
427
+ #[test]
428
+ fn test_strip_source_set_prefix_supports_clojure() {
429
+ assert_eq!(
430
+ strip_source_set_prefix(Path::new("mod/src/main/clojure/com/example/foo.clj")),
431
+ Path::new("com/example/foo.clj")
432
+ );
433
+ assert_eq!(
434
+ strip_source_set_prefix(Path::new("mod/src/main/clojure/com/example/foo.cljc")),
435
+ Path::new("com/example/foo.cljc")
436
+ );
437
+ }
438
+
439
+ #[test]
440
+ fn test_strip_source_set_prefix_handles_default_package() {
441
+ assert_eq!(
442
+ strip_source_set_prefix(Path::new("src/main/java/NoPackage.java")),
443
+ Path::new("NoPackage.java")
444
+ );
445
+ }
446
+
447
+ #[test]
448
+ fn test_strip_source_set_prefix_falls_back_when_no_match() {
449
+ // No `src/<sourceset>/<lang>/` triplet — path is returned unchanged.
450
+ assert_eq!(
451
+ strip_source_set_prefix(Path::new("sources/com/example/Foo.java")),
452
+ Path::new("sources/com/example/Foo.java")
453
+ );
454
+ assert_eq!(
455
+ strip_source_set_prefix(Path::new("Foo.java")),
456
+ Path::new("Foo.java")
457
+ );
458
+ }
459
+
460
+ #[test]
461
+ fn test_strip_source_set_prefix_does_not_match_package_named_like_lang() {
462
+ // `kotlin` as a package name (under `src/main/java/`) must not be
463
+ // mistaken for the source-set language dir.
464
+ assert_eq!(
465
+ strip_source_set_prefix(Path::new("src/main/java/com/example/kotlin/Foo.java")),
466
+ Path::new("com/example/kotlin/Foo.java")
467
+ );
468
+ }
469
+
470
+ #[test]
471
+ fn test_keeps_files_without_ambiguous_dirs() {
472
+ assert!(!is_in_ambiguous_build_dir(Path::new(
473
+ "src/main/java/com/example/Foo.java"
474
+ )));
475
+ assert!(!is_in_ambiguous_build_dir(Path::new("Foo.java")));
476
+ assert!(!is_in_ambiguous_build_dir(Path::new(
477
+ "app/src/main/java/Foo.java"
478
+ )));
479
+ }
480
+
481
+ fn fake_source(base: &str, relative: &str) -> ReleaseFileMatch {
482
+ ReleaseFileMatch {
483
+ base_path: PathBuf::from(base),
484
+ path: PathBuf::from(base).join(relative),
485
+ contents: Vec::new(),
486
+ }
487
+ }
488
+
489
+ #[test]
490
+ fn test_build_source_files_warns_on_collision_for_android_variants() {
491
+ // Sources arrive pre-sorted from `ReleaseFileSearch` (which configures
492
+ // `WalkBuilder::sort_by_file_name`); the first-seen wins in the dedup.
493
+ log_capture::setup();
494
+
495
+ let debug_src = fake_source("/app", "src/debug/java/com/example/Config.java");
496
+ let main_src = fake_source("/app", "src/main/java/com/example/Config.java");
497
+ let kept_display = debug_src.path.display().to_string();
498
+ let skipped_display = main_src.path.display().to_string();
499
+
500
+ let files = build_source_files(vec![debug_src, main_src]);
501
+
502
+ assert_eq!(files.len(), 1);
503
+ assert_eq!(files[0].url, "~/com/example/Config.jvm");
504
+ assert_eq!(files[0].path.display().to_string(), kept_display);
505
+
506
+ let warnings = log_capture::warnings();
507
+ assert_eq!(warnings.len(), 1);
508
+ let msg = &warnings[0];
509
+ assert!(
510
+ msg.contains("URL collision on ~/com/example/Config.jvm"),
511
+ "{msg}"
512
+ );
513
+ assert!(
514
+ msg.contains(&skipped_display),
515
+ "missing skipped path '{skipped_display}' in: {msg}"
516
+ );
517
+ assert!(
518
+ msg.contains(&kept_display),
519
+ "missing kept path '{kept_display}' in: {msg}"
520
+ );
521
+ }
522
+
523
+ #[test]
524
+ fn test_build_source_files_keeps_distinct_urls() {
525
+ log_capture::setup();
526
+
527
+ let sources = vec![
528
+ fake_source("/app", "src/main/java/com/example/Foo.java"),
529
+ fake_source("/app", "src/main/java/com/example/Bar.java"),
530
+ ];
531
+ let files = build_source_files(sources);
532
+ assert_eq!(files.len(), 2);
533
+ assert!(log_capture::warnings().is_empty());
534
+ }
535
+ }
@@ -21,6 +21,7 @@ pub struct ReleaseFileSearch {
21
21
  ignore_file: Option<String>,
22
22
  decompress: bool,
23
23
  respect_ignores: bool,
24
+ sort_entries: bool,
24
25
  }
25
26
 
26
27
  #[derive(Eq, PartialEq, Hash)]
@@ -39,6 +40,7 @@ impl ReleaseFileSearch {
39
40
  ignores: BTreeSet::new(),
40
41
  decompress: false,
41
42
  respect_ignores: false,
43
+ sort_entries: false,
42
44
  }
43
45
  }
44
46
 
@@ -85,6 +87,14 @@ impl ReleaseFileSearch {
85
87
  self
86
88
  }
87
89
 
90
+ /// When enabled, directory entries are yielded in sorted order, making
91
+ /// the walk deterministic across filesystems. Opt-in because callers that
92
+ /// don't care about order pay no sort cost.
93
+ pub fn sort_entries(&mut self, sort: bool) -> &mut Self {
94
+ self.sort_entries = sort;
95
+ self
96
+ }
97
+
88
98
  pub fn collect_file(path: PathBuf) -> Result<ReleaseFileMatch> {
89
99
  // NOTE: `collect_file` currently do not handle gzip decompression,
90
100
  // as its mostly used for 3rd tools like xcode or gradle.
@@ -114,6 +124,10 @@ impl ReleaseFileSearch {
114
124
  let mut builder = WalkBuilder::new(&self.path);
115
125
  builder.follow_links(true);
116
126
 
127
+ if self.sort_entries {
128
+ builder.sort_by_file_name(|a, b| a.cmp(b));
129
+ }
130
+
117
131
  if !self.respect_ignores {
118
132
  builder.git_exclude(false).git_ignore(false).ignore(false);
119
133
  }