From 351c7bba2a18e7de3e327d35621b7204d28578d0 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Mon, 28 Sep 2026 15:18:31 +0200 Subject: [PATCH 1/2] build(detekt): Apply Compose guide and lint all Kotlin modules Add the remaining settings from detekt's Compose guide. Keep accepting SCREAMING_CASE top-level constants, because the guide's PascalCase-only pattern would flag every existing TRACE_ORIGIN-style constant. sentry-compose had the detekt plugin applied but the task was always NO-SOURCE, because the default source set is src/main and a multiplatform module keeps its code in src/androidMain. Point it at the Android source sets. Also enable detekt in sentry-android-replay, where it was commented out, and in the Kotlin modules that never applied it. The newly linted modules have 298 existing findings. Record them in per-module baselines so check passes and only new issues fail. They can be fixed in follow-ups. Refs JAVA-748 Co-Authored-By: Claude Opus 5.5 (1M context) --- build.gradle.kts | 1 + detekt.yml | 10 ++ sentry-android-distribution/build.gradle.kts | 1 + .../detekt-baseline.xml | 24 +++ sentry-android-replay/build.gradle.kts | 3 +- sentry-android-replay/detekt-baseline.xml | 163 ++++++++++++++++++ sentry-apollo-3/build.gradle.kts | 1 + sentry-apollo-3/detekt-baseline.xml | 33 ++++ sentry-apollo-4/build.gradle.kts | 1 + sentry-apollo-4/detekt-baseline.xml | 34 ++++ sentry-apollo/build.gradle.kts | 1 + sentry-apollo/detekt-baseline.xml | 10 ++ sentry-compose/build.gradle.kts | 3 + sentry-compose/detekt-baseline.xml | 20 +++ sentry-ktor-client/build.gradle.kts | 1 + sentry-ktor-client/detekt-baseline.xml | 15 ++ sentry-okhttp/build.gradle.kts | 1 + sentry-okhttp/detekt-baseline.xml | 10 ++ 18 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 sentry-android-distribution/detekt-baseline.xml create mode 100644 sentry-android-replay/detekt-baseline.xml create mode 100644 sentry-apollo-3/detekt-baseline.xml create mode 100644 sentry-apollo-4/detekt-baseline.xml create mode 100644 sentry-apollo/detekt-baseline.xml create mode 100644 sentry-compose/detekt-baseline.xml create mode 100644 sentry-ktor-client/detekt-baseline.xml create mode 100644 sentry-okhttp/detekt-baseline.xml diff --git a/build.gradle.kts b/build.gradle.kts index 4f7cdb7cbb2..52e17b1d309 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -117,6 +117,7 @@ subprojects { buildUponDefaultConfig = true allRules = true config.setFrom("${rootProject.rootDir}/detekt.yml") + baseline = file("detekt-baseline.xml") } } diff --git a/detekt.yml b/detekt.yml index 09d69d77761..e8a01a87135 100644 --- a/detekt.yml +++ b/detekt.yml @@ -1,6 +1,16 @@ style: ReturnCount: max: 3 + MagicNumber: + ignorePropertyDeclaration: true + UnusedPrivateMember: + ignoreAnnotated: ['Preview'] naming: FunctionNaming: ignoreAnnotated: ['Composable'] + TopLevelPropertyNaming: + # Accept both SCREAMING_CASE and Compose's PascalCase constants. + constantPattern: '[A-Z][_A-Za-z0-9]*' +complexity: + TooManyFunctions: + ignoreAnnotatedFunctions: ['Preview'] diff --git a/sentry-android-distribution/build.gradle.kts b/sentry-android-distribution/build.gradle.kts index bd88690c1cc..074d56c08e6 100644 --- a/sentry-android-distribution/build.gradle.kts +++ b/sentry-android-distribution/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion plugins { id("com.android.library") alias(libs.plugins.kotlin.android) + alias(libs.plugins.detekt) } android { diff --git a/sentry-android-distribution/detekt-baseline.xml b/sentry-android-distribution/detekt-baseline.xml new file mode 100644 index 00000000000..78b4ed376de --- /dev/null +++ b/sentry-android-distribution/detekt-baseline.xml @@ -0,0 +1,24 @@ + + + + + MagicNumber:DistributionHttpClient.kt$DistributionHttpClient$200 + MagicNumber:DistributionHttpClient.kt$DistributionHttpClient$299 + MagicNumber:DistributionHttpClient.kt$DistributionHttpClient.HttpResponse$200 + MagicNumber:DistributionHttpClient.kt$DistributionHttpClient.HttpResponse$299 + MagicNumber:UpdateResponseParser.kt$UpdateResponseParser$200 + MagicNumber:UpdateResponseParser.kt$UpdateResponseParser$400 + MagicNumber:UpdateResponseParser.kt$UpdateResponseParser$499 + MagicNumber:UpdateResponseParser.kt$UpdateResponseParser$500 + MagicNumber:UpdateResponseParser.kt$UpdateResponseParser$599 + MaxLineLength:DistributionHttpClient.kt$DistributionHttpClient$"/api/0/projects/${URLEncoder.encode(orgSlug, "UTF-8")}/${URLEncoder.encode(projectSlug, "UTF-8")}/preprodartifacts/check-for-updates/" + SwallowedException:DistributionIntegration.kt$DistributionIntegration$e: android.content.ActivityNotFoundException + TooGenericExceptionCaught:DistributionIntegration.kt$DistributionIntegration$e: Exception + TooGenericExceptionCaught:UpdateResponseParser.kt$UpdateResponseParser$e: Exception + UseCheckOrError:DistributionHttpClient.kt$DistributionHttpClient$throw IllegalStateException( "Missing required distribution configuration: orgSlug, projectSlug, or orgAuthToken" ) + UseCheckOrError:DistributionHttpClient.kt$DistributionHttpClient$throw IllegalStateException("sentryClientName must be set") + UseCheckOrError:DistributionIntegration.kt$DistributionIntegration$throw IllegalStateException("buildConfiguration must be set in distribution options") + UseRequire:UpdateResponseParser.kt$UpdateResponseParser$throw IllegalArgumentException( "Missing required fields in API response: ${missingFields.joinToString(", ")}" ) + WildcardImport:DistributionHttpClientTest.kt$import org.junit.Assert.* + + diff --git a/sentry-android-replay/build.gradle.kts b/sentry-android-replay/build.gradle.kts index 0a40a920a7c..1318f7767c9 100644 --- a/sentry-android-replay/build.gradle.kts +++ b/sentry-android-replay/build.gradle.kts @@ -8,8 +8,7 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) alias(libs.plugins.gradle.versions) - // TODO: enable it later - // alias(libs.plugins.detekt) + alias(libs.plugins.detekt) } android { diff --git a/sentry-android-replay/detekt-baseline.xml b/sentry-android-replay/detekt-baseline.xml new file mode 100644 index 00000000000..21a07b580ad --- /dev/null +++ b/sentry-android-replay/detekt-baseline.xml @@ -0,0 +1,163 @@ + + + + + ComplexCondition:ReplayCache.kt$ReplayCache.Companion$height == null || width == null || frameRate == null || bitRate == null || (segmentId == null || segmentId == -1) || segmentTimestamp == null || replayType == null + ComplexCondition:ReplayIntegration.kt$ReplayIntegration$isManualPause || lastKnownConnectionStatus == DISCONNECTED || scopes?.rateLimiter?.isActiveForCategory(All) == true || scopes?.rateLimiter?.isActiveForCategory(Replay) == true + ComplexCondition:ReplayIntegration.kt$ReplayIntegration$name.startsWith("replay_") && !name.contains(replayId.toString()) && !(unfinishedReplayId.isNotBlank() && name.contains(unfinishedReplayId)) + ComplexCondition:ReplayIntegration.kt$ReplayIntegration$state.get().captureStrategy is SessionCaptureStrategy && (lastKnownConnectionStatus == DISCONNECTED || scopes?.rateLimiter?.isActiveForCategory(All) == true || scopes?.rateLimiter?.isActiveForCategory(Replay) == true) + ComplexCondition:ScreenshotRecorder.kt$ScreenshotRecorder$root == null || root.width <= 0 || root.height <= 0 || !root.isShown + ComplexCondition:SimpleVideoEncoder.kt$SimpleVideoEncoder$Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) || Build.MANUFACTURER.contains("motorola", ignoreCase = true) || SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER) .equals("spreadtrum", ignoreCase = true) || SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER) .equals("unisoc", ignoreCase = true) + CyclomaticComplexMethod:CaptureStrategy.kt$CaptureStrategy.Companion$@Suppress("NewApi") private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment + CyclomaticComplexMethod:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$private fun fromComposeNode( node: LayoutNode, parent: ViewHierarchyNode?, distance: Int, isComposeRoot: Boolean, options: SentryMaskingOptions, logger: ILogger, ): ViewHierarchyNode? + CyclomaticComplexMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$override fun convert(breadcrumb: Breadcrumb): RRWebEvent? + CyclomaticComplexMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$private fun Breadcrumb.toRRWebSpanEvent(): RRWebSpanEvent + CyclomaticComplexMethod:ReplayCache.kt$ReplayCache$public fun createVideoOf( duration: Long, from: Long, segmentId: Int, height: Int, width: Int, frameRate: Int, bitRate: Int, videoFile: File = File(replayCacheDir, "$segmentId.mp4"), ): GeneratedVideo? + CyclomaticComplexMethod:ReplayCache.kt$ReplayCache.Companion$internal fun fromDisk( options: SentryOptions, replayId: SentryId, replayCacheProvider: ((replayId: SentryId) -> ReplayCache)? = null, ): LastSegmentData? + CyclomaticComplexMethod:ReplayGestureConverter.kt$ReplayGestureConverter$fun convert( event: MotionEvent, recorderConfig: ScreenshotRecorderConfig, ): List<RRWebIncrementalSnapshotEvent>? + CyclomaticComplexMethod:ReplayLifecycle.kt$internal fun ReplayLifecycleState.isAllowed(newState: ReplayLifecycleState): Boolean + CyclomaticComplexMethod:SimpleVideoEncoder.kt$SimpleVideoEncoder$private fun drainCodec(endOfStream: Boolean) + EmptyCatchBlock:ReplaySmokeTest.kt$ReplaySmokeTest${} + EmptyDefaultConstructor:ScreenshotRecorderTest.kt$ScreenshotRecorderTest.Fixture$() + EmptyFunctionBlock:ReplayExecutorService.kt$CompletedFuture${} + ForbiddenComment:BufferCaptureStrategy.kt$BufferCaptureStrategy$// TODO: can be a single while-loop + ForbiddenComment:BufferCaptureStrategy.kt$BufferCaptureStrategy$// TODO: capture envelopes for buffered segments instead, but don't send them until buffer is + ForbiddenComment:CaptureStrategy.kt$CaptureStrategy.Companion$// TODO: support non-fullscreen windows later + ForbiddenComment:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$// TODO: if we get reports that it's slow, we can drop this, and just mask + ForbiddenComment:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$// TODO: support editable text (currently there's a way to get @Composable's padding only + ForbiddenComment:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$// TODO: the whole view instead of per-line + ForbiddenComment:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$// TODO: this currently does not support embedded AndroidViews, we'd have to + ForbiddenComment:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$// TODO: traverse the ViewHierarchyNode here again. For now we can recommend + ForbiddenComment:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$// TODO: using custom modifiers to obscure the entire node if it's sensitive + ForbiddenComment:Nodes.kt$// TODO: probably most of the below we can do via bytecode instrumentation and speed up at runtime + ForbiddenComment:Persistable.kt$PersistableLinkedList$// TODO: enable this back after we are able to serialize individual touches to disk to not overload + ForbiddenComment:PixelCopyStrategy.kt$PixelCopyStrategy$// TODO: disableAllMasking here and dont traverse? + ForbiddenComment:ReplayCache.kt$ReplayCache$// TODO: it's awful, choose a better serialization format + ForbiddenComment:ReplayIntegration.kt$ReplayIntegration$// TODO: previous run and set them directly to the ReplayEvent so they don't get overwritten in + ForbiddenComment:ReplayIntegration.kt$ReplayIntegration$// TODO: read persisted options/scope values form the + ForbiddenComment:ReplaySmokeTest.kt$ReplaySmokeTest$// TODO: figure out why there's more than 10 + ForbiddenComment:SimpleVideoEncoder.kt$SimpleVideoEncoder$// TODO: because the screenshot config has to match the video config + ForbiddenComment:SimpleVideoEncoder.kt$SimpleVideoEncoder$// TODO: if this ever becomes a problem, move this to ScreenshotRecorderConfig.from() + ForbiddenComment:SimpleVideoEncoder.kt$SimpleVideoEncoder$// TODO: maybe enable this back later, for now variable bitrate seems to provide much better + ForbiddenComment:SimpleVideoEncoder.kt$SimpleVideoEncoder$// TODO: quality with almost no overhead in terms of video size, let's monitor that + ForbiddenComment:Views.kt$// TODO: maybe find a way how to check if the drawable is coming from the apk or loaded from + ForbiddenComment:Views.kt$// TODO: otherwise maybe check for the bitmap size and don't mask those that take a lot of height + FunctionParameterNaming:PixelCopyStrategyTest.kt$DeferredWindowPixelCopyShadow.Companion$_dest: Bitmap + FunctionParameterNaming:PixelCopyStrategyTest.kt$DeferredWindowPixelCopyShadow.Companion$_source: Window + LargeClass:CanvasStrategy.kt$TextIgnoringDelegateCanvas : Canvas + LargeClass:ReplayIntegrationTest.kt$ReplayIntegrationTest + LongMethod:CaptureStrategy.kt$CaptureStrategy.Companion$@Suppress("NewApi") private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment + LongMethod:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$private fun fromComposeNode( node: LayoutNode, parent: ViewHierarchyNode?, distance: Int, isComposeRoot: Boolean, options: SentryMaskingOptions, logger: ILogger, ): ViewHierarchyNode? + LongMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$override fun convert(breadcrumb: Breadcrumb): RRWebEvent? + LongMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$private fun Breadcrumb.toRRWebSpanEvent(): RRWebSpanEvent + LongMethod:PixelCopyStrategy.kt$PixelCopyStrategy$@SuppressLint("NewApi") override fun capture(root: View) + LongMethod:ReplayCache.kt$ReplayCache$public fun createVideoOf( duration: Long, from: Long, segmentId: Int, height: Int, width: Int, frameRate: Int, bitRate: Int, videoFile: File = File(replayCacheDir, "$segmentId.mp4"), ): GeneratedVideo? + LongMethod:ReplayCache.kt$ReplayCache.Companion$internal fun fromDisk( options: SentryOptions, replayId: SentryId, replayCacheProvider: ((replayId: SentryId) -> ReplayCache)? = null, ): LastSegmentData? + LongMethod:ReplayGestureConverter.kt$ReplayGestureConverter$fun convert( event: MotionEvent, recorderConfig: ScreenshotRecorderConfig, ): List<RRWebIncrementalSnapshotEvent>? + LongMethod:ReplayGestureConverterTest.kt$ReplayGestureConverterTest$@Test fun `convert multi-pointer events`() + LongMethod:ReplayIntegrationTest.kt$ReplayIntegrationTest$@Test fun `register finalizes previous replay`() + LongMethod:ReplayIntegrationWithRecorderTest.kt$ReplayIntegrationWithRecorderTest$@Test fun `works with different recorder`() + LongMethod:SimpleVideoEncoder.kt$SimpleVideoEncoder$private fun drainCodec(endOfStream: Boolean) + LongMethod:ViewHierarchyNode.kt$ViewHierarchyNode.Companion$@JvmStatic fun fromView( view: View, parent: ViewHierarchyNode?, distance: Int, options: SentryMaskingOptions, ): ViewHierarchyNode + LongParameterList:BaseCaptureStrategy.kt$BaseCaptureStrategy$( duration: Long, currentSegmentTimestamp: Date, replayId: SentryId, segmentId: Int, height: Int, width: Int, frameRate: Int, bitRate: Int, replayType: ReplayType = this.replayType, cache: ReplayCache? = this.cache, screenAtStart: String? = this.screenAtStart, breadcrumbs: List<Breadcrumb>? = null, events: Deque<RRWebEvent> = this.currentEvents, ) + LongParameterList:CaptureStrategy.kt$CaptureStrategy.Companion$( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ) + LongParameterList:CaptureStrategy.kt$CaptureStrategy.Companion$( scopes: IScopes?, options: SentryOptions, duration: Long, currentSegmentTimestamp: Date, replayId: SentryId, segmentId: Int, height: Int, width: Int, replayType: ReplayType, cache: ReplayCache?, frameRate: Int, bitRate: Int, screenAtStart: String?, breadcrumbs: List<Breadcrumb>?, events: Deque<RRWebEvent>, traceIds: List<String> = emptyList(), segmentNames: List<String> = emptyList(), ) + LongParameterList:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$( node: LayoutNode, parent: ViewHierarchyNode?, distance: Int, isComposeRoot: Boolean, options: SentryMaskingOptions, logger: ILogger, ) + LongParameterList:PixelCopyStrategy.kt$( destCanvas: Canvas, destPaint: Paint, tmpSrc: Rect, tmpDst: RectF, sourceBitmap: Bitmap, sourceX: Int, sourceY: Int, windowX: Int, windowY: Int, scaleFactorX: Float, scaleFactorY: Float, ) + LongParameterList:PixelCopyStrategy.kt$PixelCopyStrategy$( root: View, captures: Array<SurfaceViewCapture?>, viewHierarchy: ViewHierarchyNode, windowX: Int, windowY: Int, resetUnstableCaptures: Boolean, ) + LongParameterList:ReplayCache.kt$ReplayCache$( duration: Long, from: Long, segmentId: Int, height: Int, width: Int, frameRate: Int, bitRate: Int, videoFile: File = File(replayCacheDir, "$segmentId.mp4"), ) + LongParameterList:ReplayIntegration.kt$ReplayIntegration$( context: Context, dateProvider: ICurrentDateProvider, recorderProvider: (() -> Recorder)?, replayCacheProvider: ((replayId: SentryId) -> ReplayCache)?, replayCaptureStrategyProvider: ((isFullSession: Boolean) -> CaptureStrategy)? = null, mainLooperHandler: MainLooperHandler? = null, gestureRecorderProvider: (() -> GestureRecorder)? = null, ) + LongParameterList:ReplayIntegrationTest.kt$ReplayIntegrationTest.Fixture$( context: Context, sessionSampleRate: Double = 1.0, onErrorSampleRate: Double = 1.0, isRateLimited: Boolean = false, recorderProvider: (() -> Recorder)? = null, replayCaptureStrategyProvider: ((isFullSession: Boolean) -> CaptureStrategy)? = null, gestureRecorderProvider: (() -> GestureRecorder)? = null, dateProvider: ICurrentDateProvider = CurrentDateProvider.getInstance(), mainLooperHandler: MainLooperHandler = mock { doAnswer { (it.arguments[0] as Runnable).run() true } .whenever(mock) .post(any()) }, ) + LongParameterList:ViewHierarchyNode.kt$ViewHierarchyNode$( val x: Float, val y: Float, val width: Int, val height: Int, // Elevation (in px) val elevation: Float, // Distance to the parent (index) val distance: Int, val parent: ViewHierarchyNode? = null, val shouldMask: Boolean = false, // Whether the node is important for content capture (=non-empty container) var isImportantForContentCapture: Boolean = false, val isVisible: Boolean = false, val visibleRect: Rect? = null, ) + LongParameterList:ViewHierarchyNode.kt$ViewHierarchyNode.GenericViewHierarchyNode$( x: Float, y: Float, width: Int, height: Int, elevation: Float, distance: Int, parent: ViewHierarchyNode? = null, shouldMask: Boolean = false, isImportantForContentCapture: Boolean = false, isVisible: Boolean = false, visibleRect: Rect? = null, ) + LongParameterList:ViewHierarchyNode.kt$ViewHierarchyNode.ImageViewHierarchyNode$( x: Float, y: Float, width: Int, height: Int, elevation: Float, distance: Int, parent: ViewHierarchyNode? = null, shouldMask: Boolean = false, isImportantForContentCapture: Boolean = false, isVisible: Boolean = false, visibleRect: Rect? = null, ) + LongParameterList:ViewHierarchyNode.kt$ViewHierarchyNode.SurfaceViewHierarchyNode$( val surfaceViewRef: WeakReference<SurfaceView>, x: Float, y: Float, width: Int, height: Int, elevation: Float, distance: Int, parent: ViewHierarchyNode? = null, shouldMask: Boolean = false, isImportantForContentCapture: Boolean = false, isVisible: Boolean = false, visibleRect: Rect? = null, ) + LongParameterList:ViewHierarchyNode.kt$ViewHierarchyNode.TextViewHierarchyNode$( val layout: TextLayout? = null, val dominantColor: Int? = null, val paddingLeft: Int = 0, val paddingTop: Int = 0, x: Float, y: Float, width: Int, height: Int, elevation: Float, distance: Int, parent: ViewHierarchyNode? = null, shouldMask: Boolean = false, isImportantForContentCapture: Boolean = false, isVisible: Boolean = false, visibleRect: Rect? = null, ) + LoopWithTooManyJumpStatements:PixelCopyStrategy.kt$PixelCopyStrategy$for + LoopWithTooManyJumpStatements:ReplayCache.kt$ReplayCache$while + LoopWithTooManyJumpStatements:SimpleVideoEncoder.kt$SimpleVideoEncoder$while + MagicNumber:BaseCaptureStrategy.kt$BaseCaptureStrategy$26 + MagicNumber:BufferCaptureStrategy.kt$BufferCaptureStrategy$26 + MagicNumber:CanvasStrategy.kt$TextIgnoringDelegateCanvas$100 + MagicNumber:CanvasStrategy.kt$TextIgnoringDelegateCanvas$10f + MagicNumber:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$26 + MagicNumber:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$1000.0 + MagicNumber:Persistable.kt$PersistableLinkedList$26 + MagicNumber:ReplayCache.kt$ReplayCache$1000 + MagicNumber:ReplayCache.kt$ReplayCache.Companion$1000 + MagicNumber:ReplayGestureConverter.kt$ReplayGestureConverter$10 + MagicNumber:ScreenshotRecorder.kt$ScreenshotRecorder$26 + MagicNumber:ScreenshotRecorder.kt$ScreenshotRecorderConfig.Companion$16 + MagicNumber:ScreenshotRecorder.kt$ScreenshotRecorderConfig.Companion$8 + MagicNumber:SimpleVideoEncoder.kt$MuxerConfig$24 + MagicNumber:SimpleVideoEncoder.kt$SimpleVideoEncoder$26 + MagicNumber:ViewHierarchyNode.kt$ViewHierarchyNode$26 + MagicNumber:Views.kt$10 + MagicNumber:Views.kt$21 + MagicNumber:WindowRecorder.kt$WindowRecorder$100L + MagicNumber:WindowRecorder.kt$WindowRecorder$26 + MagicNumber:WindowRecorder.kt$WindowRecorder.Capturer$1000L + MatchingDeclarationName:ModifierExtensions.kt$SentryReplayModifiers + MatchingDeclarationName:Nodes.kt$ComposeTextLayout : TextLayout + MatchingDeclarationName:Persistable.kt$PersistableLinkedList : ConcurrentLinkedDeque + MatchingDeclarationName:ReplayLifecycle.kt$ReplayLifecycleState + MaxLineLength:ComposeMaskingOptionsTest.kt$ComposeMaskingOptionsTest$"Flaky: Robolectric intermittently reports zero bounds for nodes, causing isVisible=false and making the assertion non-deterministic" + NestedBlockDepth:CaptureStrategy.kt$CaptureStrategy.Companion$@Suppress("NewApi") private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment + NestedBlockDepth:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$private fun Breadcrumb.toRRWebSpanEvent(): RRWebSpanEvent + NestedBlockDepth:Nodes.kt$internal fun LayoutNode.findPainter(): Painter? + NestedBlockDepth:Nodes.kt$internal fun LayoutNode.findTextColor(): Color? + NestedBlockDepth:ReplayGestureConverter.kt$ReplayGestureConverter$fun convert( event: MotionEvent, recorderConfig: ScreenshotRecorderConfig, ): List<RRWebIncrementalSnapshotEvent>? + NestedBlockDepth:SimpleVideoEncoder.kt$SimpleVideoEncoder$private fun drainCodec(endOfStream: Boolean) + ReturnCount:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$fun fromView( view: View, parent: ViewHierarchyNode?, options: SentryMaskingOptions, logger: ILogger, ): Boolean + ReturnCount:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$private fun SemanticsConfiguration?.shouldMask( isImage: Boolean, options: SentryMaskingOptions, ): Boolean + ReturnCount:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$override fun convert(breadcrumb: Breadcrumb): RRWebEvent? + ReturnCount:ReplayCache.kt$ReplayCache.Companion$internal fun fromDisk( options: SentryOptions, replayId: SentryId, replayCacheProvider: ((replayId: SentryId) -> ReplayCache)? = null, ): LastSegmentData? + ReturnCount:ReplayGestureConverter.kt$ReplayGestureConverter$fun convert( event: MotionEvent, recorderConfig: ScreenshotRecorderConfig, ): List<RRWebIncrementalSnapshotEvent>? + ReturnCount:ReplayIntegration.kt$ReplayIntegration$override fun captureReplay(isTerminating: Boolean?): SentryId + ReturnCount:ScreenshotRecorder.kt$ScreenshotRecorder$fun capture() + ReturnCount:SentryLayoutNodeHelper.kt$SentryLayoutNodeHelper$@Suppress("UNCHECKED_CAST") fun getChildren(node: LayoutNode): List<LayoutNode> + ReturnCount:SentryLayoutNodeHelper.kt$SentryLayoutNodeHelper$fun isTransparent(node: LayoutNode): Boolean + ReturnCount:ViewHierarchyNode.kt$ViewHierarchyNode.Companion$@JvmStatic fun fromView( view: View, parent: ViewHierarchyNode?, distance: Int, options: SentryMaskingOptions, ): ViewHierarchyNode + ReturnCount:ViewHierarchyNode.kt$ViewHierarchyNode.Companion$private fun View.shouldMask(options: SentryMaskingOptions): Boolean + ReturnCount:Views.kt$internal fun View.isVisibleToUser(): Pair<Boolean, Rect?> + SwallowedException:Nodes.kt$e: Throwable + SwallowedException:ReplayCache.kt$ReplayCache.Companion$e: Throwable + SwallowedException:ReplaySmokeTest.kt$ReplaySmokeTest$e: ConditionTimeoutException + SwallowedException:Views.kt$e: NullPointerException + ThrowsCount:SimpleVideoEncoder.kt$SimpleVideoEncoder$private fun drainCodec(endOfStream: Boolean) + TooGenericExceptionCaught:BaseCaptureStrategy.kt$BaseCaptureStrategy.<no name provided>$e: Throwable + TooGenericExceptionCaught:BufferCaptureStrategy.kt$BufferCaptureStrategy$e: Throwable + TooGenericExceptionCaught:CanvasStrategy.kt$CanvasStrategy$t: Throwable + TooGenericExceptionCaught:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$e: Throwable + TooGenericExceptionCaught:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$t: Throwable + TooGenericExceptionCaught:Executors.kt$e: Throwable + TooGenericExceptionCaught:GestureRecorder.kt$GestureRecorder.SentryReplayGestureRecorder$e: Throwable + TooGenericExceptionCaught:Nodes.kt$e: Throwable + TooGenericExceptionCaught:PixelCopyStrategy.kt$PixelCopyStrategy$e: RuntimeException + TooGenericExceptionCaught:PixelCopyStrategy.kt$PixelCopyStrategy$e: Throwable + TooGenericExceptionCaught:ReplayCache.kt$ReplayCache$e: Throwable + TooGenericExceptionCaught:ReplayCache.kt$ReplayCache$t: Throwable + TooGenericExceptionCaught:ReplayCache.kt$ReplayCache.Companion$e: Throwable + TooGenericExceptionCaught:ReplayExecutorService.kt$ReplayExecutorService$e: Throwable + TooGenericExceptionCaught:ReplayIntegration.kt$ReplayIntegration$e: Throwable + TooGenericExceptionCaught:ScreenshotRecorder.kt$ScreenshotRecorder$e: Throwable + TooGenericExceptionCaught:SimpleVideoEncoder.kt$SimpleVideoEncoder$e: RuntimeException + TooGenericExceptionCaught:SimpleVideoEncoder.kt$SimpleVideoEncoder$e: Throwable + TooGenericExceptionCaught:Views.kt$e: NullPointerException + TooGenericExceptionCaught:WindowRecorder.kt$WindowRecorder.Capturer$e: Throwable + TooGenericExceptionThrown:ReplayIntegrationTest.kt$ReplayIntegrationTest$throw RuntimeException("test") + TooGenericExceptionThrown:SimpleVideoEncoder.kt$SimpleVideoEncoder$throw RuntimeException("encoderOutputBuffer $encoderStatus was null") + TooGenericExceptionThrown:SimpleVideoEncoder.kt$SimpleVideoEncoder$throw RuntimeException("format changed twice") + TooGenericExceptionThrown:SimpleVideoEncoder.kt$SimpleVideoEncoder$throw RuntimeException("muxer hasn't started") + TooManyFunctions:BaseCaptureStrategy.kt$BaseCaptureStrategy : CaptureStrategy + TooManyFunctions:BufferCaptureStrategy.kt$BufferCaptureStrategy : BaseCaptureStrategy + TooManyFunctions:CanvasStrategy.kt$TextIgnoringDelegateCanvas : Canvas + TooManyFunctions:CaptureStrategy.kt$CaptureStrategy + TooManyFunctions:PixelCopyStrategy.kt$PixelCopyStrategy : ScreenshotStrategy + TooManyFunctions:ReplayIntegration.kt$ReplayIntegration : IntegrationCloseableScreenshotRecorderCallbackTouchRecorderCallbackReplayControllerIConnectionStatusObserverIRateLimitObserverWindowCallback + TooManyFunctions:WindowRecorder.kt$WindowRecorder : RecorderOnRootViewsChangedListenerExecutorProvider + UnusedPrivateProperty:DebugOverlayDrawable.kt$DebugOverlayDrawable$private val padding = 6f + UseCheckOrError:ReplayShadowMediaCodec.kt$ReplayShadowMediaCodec$throw IllegalStateException("Simulated codec start failure") + UtilityClassWithPublicConstructor:PixelCopyStrategyTest.kt$DeferredWindowPixelCopyShadow + + diff --git a/sentry-apollo-3/build.gradle.kts b/sentry-apollo-3/build.gradle.kts index 70f43d946ef..357d5224495 100644 --- a/sentry-apollo-3/build.gradle.kts +++ b/sentry-apollo-3/build.gradle.kts @@ -5,6 +5,7 @@ plugins { `java-library` id("io.sentry.javadoc") alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.detekt) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) alias(libs.plugins.buildconfig) diff --git a/sentry-apollo-3/detekt-baseline.xml b/sentry-apollo-3/detekt-baseline.xml new file mode 100644 index 00000000000..6bb8edffb36 --- /dev/null +++ b/sentry-apollo-3/detekt-baseline.xml @@ -0,0 +1,33 @@ + + + + + ClassNaming:LaunchDetailsQuery_ResponseAdapter.kt$LaunchDetailsQuery_ResponseAdapter + ClassNaming:LaunchDetailsQuery_VariablesAdapter.kt$LaunchDetailsQuery_VariablesAdapter : Adapter + CyclomaticComplexMethod:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + LongMethod:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + LongParameterList:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$( span: ISpan?, request: HttpRequest, response: HttpResponse?, statusCode: Int?, operationName: String?, operationType: String?, operationId: String?, ) + LongParameterList:SentryApollo3InterceptorClientErrors.kt$SentryApollo3InterceptorClientErrors.Fixture$( captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS, failedRequestTargets: List<String> = listOf(DEFAULT_PROPAGATION_TARGETS), httpStatusCode: Int = 200, responseBody: String = responseBodyOk, sendDefaultPii: Boolean = false, includeCookies: Boolean = sendDefaultPii, socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, configureOptions: SentryOptions.() -> Unit = {}, ) + MaxLineLength:LaunchDetailsQuery.kt$LaunchDetailsQuery.Companion$"query LaunchDetails(${'$'}id: ID!) { launch(id: ${'$'}id) { id site mission { name missionPatch(size: LARGE) } rocket { name type } } }" + MaxLineLength:SentryApollo3InterceptorTest.kt$SentryApollo3InterceptorTest$"thirdPartyBaggage=someValue,secondThirdPartyBaggage=secondValue; property;propertyKey=propertyValue,anotherThirdPartyBaggage=anotherValue" + MaxLineLength:SentryApollo3InterceptorTest.kt$SentryApollo3InterceptorTest.Fixture$"secondThirdPartyBaggage=secondValue; property;propertyKey=propertyValue,anotherThirdPartyBaggage=anotherValue" + NestedBlockDepth:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + NestedBlockDepth:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$private fun finish( span: ISpan?, request: HttpRequest, response: HttpResponse?, statusCode: Int?, operationName: String?, operationType: String?, operationId: String?, ) + NestedBlockDepth:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$private fun maybeAddTracingHeaders( scopes: IScopes, request: HttpRequest, span: ISpan?, ): HttpRequest + ReturnCount:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + SwallowedException:SentryApollo3InterceptorClientErrors.kt$SentryApollo3InterceptorClientErrors$e: ApolloException + SwallowedException:SentryApollo3InterceptorTest.kt$SentryApollo3InterceptorTest$e: ApolloException + SwallowedException:SentryApollo3InterceptorWithVariablesTest.kt$SentryApollo3InterceptorWithVariablesTest$e: ApolloException + TooGenericExceptionCaught:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor$e: Throwable + TooGenericExceptionThrown:SentryApollo3InterceptorTest.kt$SentryApollo3InterceptorTest$throw RuntimeException() + TooManyFunctions:SentryApollo3HttpInterceptor.kt$SentryApollo3HttpInterceptor : HttpInterceptor + UseCheckOrError:LaunchDetailsQuery_VariablesAdapter.kt$LaunchDetailsQuery_VariablesAdapter$throw IllegalStateException("Input type used in output position") + UtilityClassWithPublicConstructor:GraphQLBoolean.kt$GraphQLBoolean + UtilityClassWithPublicConstructor:GraphQLID.kt$GraphQLID + UtilityClassWithPublicConstructor:GraphQLString.kt$GraphQLString + UtilityClassWithPublicConstructor:Launch.kt$Launch + UtilityClassWithPublicConstructor:Mission.kt$Mission + UtilityClassWithPublicConstructor:Query.kt$Query + UtilityClassWithPublicConstructor:Rocket.kt$Rocket + + diff --git a/sentry-apollo-4/build.gradle.kts b/sentry-apollo-4/build.gradle.kts index 4f1276f0bf4..078d26b8ff8 100644 --- a/sentry-apollo-4/build.gradle.kts +++ b/sentry-apollo-4/build.gradle.kts @@ -5,6 +5,7 @@ plugins { `java-library` id("io.sentry.javadoc") alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.detekt) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) alias(libs.plugins.buildconfig) diff --git a/sentry-apollo-4/detekt-baseline.xml b/sentry-apollo-4/detekt-baseline.xml new file mode 100644 index 00000000000..371c726cb9f --- /dev/null +++ b/sentry-apollo-4/detekt-baseline.xml @@ -0,0 +1,34 @@ + + + + + ClassNaming:LaunchDetailsQuery_ResponseAdapter.kt$LaunchDetailsQuery_ResponseAdapter + ClassNaming:LaunchDetailsQuery_VariablesAdapter.kt$LaunchDetailsQuery_VariablesAdapter : Adapter + CyclomaticComplexMethod:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + LongMethod:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + LongParameterList:SentryApollo4BuilderExtensionsClientErrorsTest.kt$SentryApollo4BuilderExtensionsClientErrorsTest.Fixture$( captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS, failedRequestTargets: List<String> = listOf(DEFAULT_PROPAGATION_TARGETS), httpStatusCode: Int = 200, responseBody: String = responseBodyOk, sendDefaultPii: Boolean = false, includeCookies: Boolean = sendDefaultPii, socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, configureOptions: SentryOptions.() -> Unit = {}, ) + LongParameterList:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$( span: ISpan?, request: HttpRequest, response: HttpResponse?, statusCode: Int?, operationName: String?, operationType: String?, operationId: String?, ) + MaxLineLength:LaunchDetailsQuery.kt$LaunchDetailsQuery.Companion$"query LaunchDetails(${'$'}id: ID!) { launch(id: ${'$'}id) { id site mission { name missionPatch(size: LARGE) } rocket { name type } } }" + MaxLineLength:LaunchDetailsQuerySelections.kt$LaunchDetailsQuerySelections$CompiledArgument.Builder(CompiledArgumentDefinition.Builder("size").build()).value("LARGE").build() + MaxLineLength:SentryApollo4HttpInterceptorTest.kt$SentryApollo4HttpInterceptorTest$"thirdPartyBaggage=someValue,secondThirdPartyBaggage=secondValue; property;propertyKey=propertyValue,anotherThirdPartyBaggage=anotherValue" + MaxLineLength:SentryApollo4HttpInterceptorTest.kt$SentryApollo4HttpInterceptorTest.Fixture$"secondThirdPartyBaggage=secondValue; property;propertyKey=propertyValue,anotherThirdPartyBaggage=anotherValue" + NestedBlockDepth:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + NestedBlockDepth:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$private fun finish( span: ISpan?, request: HttpRequest, response: HttpResponse?, statusCode: Int?, operationName: String?, operationType: String?, operationId: String?, ) + NestedBlockDepth:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$private fun maybeAddTracingHeaders( scopes: IScopes, request: HttpRequest, span: ISpan?, ): HttpRequest + ReturnCount:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$private fun captureEvent( request: HttpRequest, response: HttpResponse, operationName: String?, operationType: String?, ) + SwallowedException:SentryApollo4BuilderExtensionsClientErrorsTest.kt$SentryApollo4BuilderExtensionsClientErrorsTest$e: ApolloException + SwallowedException:SentryApollo4BuilderExtensionsTest.kt$SentryApollo4BuilderExtensionsTest$e: ApolloException + SwallowedException:SentryApollo4HttpInterceptorTest.kt$SentryApollo4HttpInterceptorTest$e: ApolloException + TooGenericExceptionCaught:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor$e: Throwable + TooGenericExceptionThrown:SentryApollo4HttpInterceptorTest.kt$SentryApollo4HttpInterceptorTest$throw RuntimeException() + TooManyFunctions:SentryApollo4HttpInterceptor.kt$SentryApollo4HttpInterceptor : HttpInterceptor + UseCheckOrError:LaunchDetailsQuery_VariablesAdapter.kt$LaunchDetailsQuery_VariablesAdapter$throw IllegalStateException("Input type used in output position") + UtilityClassWithPublicConstructor:GraphQLBoolean.kt$GraphQLBoolean + UtilityClassWithPublicConstructor:GraphQLID.kt$GraphQLID + UtilityClassWithPublicConstructor:GraphQLString.kt$GraphQLString + UtilityClassWithPublicConstructor:Launch.kt$Launch + UtilityClassWithPublicConstructor:Mission.kt$Mission + UtilityClassWithPublicConstructor:Query.kt$Query + UtilityClassWithPublicConstructor:Rocket.kt$Rocket + + diff --git a/sentry-apollo/build.gradle.kts b/sentry-apollo/build.gradle.kts index 2da8d8b20c1..570214e60b8 100644 --- a/sentry-apollo/build.gradle.kts +++ b/sentry-apollo/build.gradle.kts @@ -5,6 +5,7 @@ plugins { `java-library` id("io.sentry.javadoc") alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.detekt) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) alias(libs.plugins.buildconfig) diff --git a/sentry-apollo/detekt-baseline.xml b/sentry-apollo/detekt-baseline.xml new file mode 100644 index 00000000000..d010e1a4336 --- /dev/null +++ b/sentry-apollo/detekt-baseline.xml @@ -0,0 +1,10 @@ + + + + + EmptyFunctionBlock:SentryApolloInterceptor.kt$SentryApolloInterceptor${} + SwallowedException:SentryApolloInterceptorTest.kt$SentryApolloInterceptorTest$e: ApolloException + TooGenericExceptionCaught:SentryApolloInterceptor.kt$SentryApolloInterceptor$e: Exception + TooGenericExceptionThrown:SentryApolloInterceptorTest.kt$SentryApolloInterceptorTest$throw RuntimeException() + + diff --git a/sentry-compose/build.gradle.kts b/sentry-compose/build.gradle.kts index c5bb5715830..394885df1b8 100644 --- a/sentry-compose/build.gradle.kts +++ b/sentry-compose/build.gradle.kts @@ -121,6 +121,9 @@ android { } } +// The default detekt source set is src/{main,test}, which doesn't exist in a multiplatform module. +detekt { source.setFrom("src/androidMain/kotlin", "src/androidUnitTest/kotlin") } + tasks.withType().configureEach { // Target version of the generated JVM bytecode. It is used for type resolution. jvmTarget = JavaVersion.VERSION_1_8.toString() diff --git a/sentry-compose/detekt-baseline.xml b/sentry-compose/detekt-baseline.xml new file mode 100644 index 00000000000..b59189497e9 --- /dev/null +++ b/sentry-compose/detekt-baseline.xml @@ -0,0 +1,20 @@ + + + + + CyclomaticComplexMethod:ComposeGestureTargetLocator.kt$ComposeGestureTargetLocator$override fun locate(root: Any?, x: Float, y: Float, targetType: UiElement.Type): UiElement? + FunctionOnlyReturningConstant:SentryModifier.kt$SentryModifier.SentryTagModifierNode$@Suppress("unused") fun isImportantForBounds(): Boolean + LongMethod:ComposeGestureTargetLocator.kt$ComposeGestureTargetLocator$override fun locate(root: Any?, x: Float, y: Float, targetType: UiElement.Type): UiElement? + LongParameterList:ComposeGestureTargetLocatorTest.kt$ComposeGestureTargetLocatorTest$( isPlaced: Boolean, tag: String?, width: Int, height: Int, children: List<LayoutNode> = emptyList(), semanticsKeys: List<String> = emptyList(), nodeModifierClassName: String? = null, left: Float = 0f, top: Float = 0f, ) + LongParameterList:SentryComposeTracing.kt$BucketSpans$( ownerSpan: ISpan, startTimestamp: SentryDate, cached: WeakReference<ISpan>?, operation: String, description: String, setCached: (WeakReference<ISpan>) -> Unit, ) + NestedBlockDepth:ComposeGestureTargetLocator.kt$ComposeGestureTargetLocator$override fun locate(root: Any?, x: Float, y: Float, targetType: UiElement.Type): UiElement? + NestedBlockDepth:SentryComposeHelper.kt$SentryComposeHelper$fun extractTag(modifier: Modifier): String? + ReturnCount:ComposeGestureTargetLocator.kt$ComposeGestureTargetLocator$override fun locate(root: Any?, x: Float, y: Float, targetType: UiElement.Type): UiElement? + ReturnCount:ComposeIntegrationTests.kt$ComposeIntegrationTests$private fun locateAndroidComposeView(root: View?): Any? + ReturnCount:SentryComposeHelper.kt$SentryComposeHelper$fun extractTag(modifier: Modifier): String? + SwallowedException:SentryComposeHelper.kt$SentryComposeHelper$e: Throwable + SwallowedException:SentryComposeHelper.kt$SentryComposeHelper.Companion$e: Exception + TooGenericExceptionCaught:SentryComposeHelper.kt$SentryComposeHelper$e: Throwable + TooGenericExceptionCaught:SentryComposeHelper.kt$SentryComposeHelper.Companion$e: Exception + + diff --git a/sentry-ktor-client/build.gradle.kts b/sentry-ktor-client/build.gradle.kts index fefcdbfebaf..a1eb9150d6f 100644 --- a/sentry-ktor-client/build.gradle.kts +++ b/sentry-ktor-client/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `java-library` alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.detekt) id("io.sentry.javadoc") alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) diff --git a/sentry-ktor-client/detekt-baseline.xml b/sentry-ktor-client/detekt-baseline.xml new file mode 100644 index 00000000000..23405d6fda1 --- /dev/null +++ b/sentry-ktor-client/detekt-baseline.xml @@ -0,0 +1,15 @@ + + + + + MagicNumber:SentryKtorClientUtils.kt$SentryKtorClientUtils$1000L + MaxLineLength:SentryKtorClientPluginTest.kt$SentryKtorClientPluginTest$fun + TooGenericExceptionCaught:SentryKtorClientPlugin.kt$t: Throwable + UnusedParameter:SentryKtorClientPluginTest.kt$SentryKtorClientPluginTest.Fixture$beforeSpan: SentryKtorClientPluginConfig.BeforeSpanCallback? = null + WildcardImport:SentryKtorClientPlugin.kt$import io.ktor.client.plugins.api.* + WildcardImport:SentryKtorClientPlugin.kt$import io.ktor.client.request.* + WildcardImport:SentryKtorClientPlugin.kt$import io.ktor.client.statement.* + WildcardImport:SentryKtorClientPlugin.kt$import io.ktor.util.* + WildcardImport:SentryKtorClientPlugin.kt$import io.ktor.util.pipeline.* + + diff --git a/sentry-okhttp/build.gradle.kts b/sentry-okhttp/build.gradle.kts index 94575f54984..5abd1cf26b9 100644 --- a/sentry-okhttp/build.gradle.kts +++ b/sentry-okhttp/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `java-library` alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.detekt) id("io.sentry.javadoc") alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) diff --git a/sentry-okhttp/detekt-baseline.xml b/sentry-okhttp/detekt-baseline.xml new file mode 100644 index 00000000000..b0d75302aa0 --- /dev/null +++ b/sentry-okhttp/detekt-baseline.xml @@ -0,0 +1,10 @@ + + + + + CyclomaticComplexMethod:SentryOkHttpInterceptor.kt$SentryOkHttpInterceptor$@Suppress("LongMethod") @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response + NestedBlockDepth:SentryOkHttpInterceptor.kt$SentryOkHttpInterceptor$@Suppress("LongMethod") @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response + ReturnCount:SentryOkHttpInterceptor.kt$SentryOkHttpInterceptor$private fun shouldCaptureClientError(request: Request, response: Response): Boolean + TooGenericExceptionCaught:SentryOkHttpInterceptor.kt$SentryOkHttpInterceptor$e: Exception + + From 5181f6b8c0d66538ca79de894acabb6844e6a072 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Mon, 28 Sep 2026 15:22:36 +0200 Subject: [PATCH 2/2] build(detekt): Move Compose guide settings to a separate PR Regenerate the baselines against main's config so this PR doesn't depend on the config change. Co-Authored-By: Claude Opus 5.5 (1M context) --- detekt.yml | 10 ---------- sentry-android-replay/detekt-baseline.xml | 2 ++ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/detekt.yml b/detekt.yml index e8a01a87135..09d69d77761 100644 --- a/detekt.yml +++ b/detekt.yml @@ -1,16 +1,6 @@ style: ReturnCount: max: 3 - MagicNumber: - ignorePropertyDeclaration: true - UnusedPrivateMember: - ignoreAnnotated: ['Preview'] naming: FunctionNaming: ignoreAnnotated: ['Composable'] - TopLevelPropertyNaming: - # Accept both SCREAMING_CASE and Compose's PascalCase constants. - constantPattern: '[A-Z][_A-Za-z0-9]*' -complexity: - TooManyFunctions: - ignoreAnnotatedFunctions: ['Preview'] diff --git a/sentry-android-replay/detekt-baseline.xml b/sentry-android-replay/detekt-baseline.xml index 21a07b580ad..54c6754e2d8 100644 --- a/sentry-android-replay/detekt-baseline.xml +++ b/sentry-android-replay/detekt-baseline.xml @@ -81,6 +81,7 @@ MagicNumber:CanvasStrategy.kt$TextIgnoringDelegateCanvas$100 MagicNumber:CanvasStrategy.kt$TextIgnoringDelegateCanvas$10f MagicNumber:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$26 + MagicNumber:DebugOverlayDrawable.kt$DebugOverlayDrawable$6f MagicNumber:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$1000.0 MagicNumber:Persistable.kt$PersistableLinkedList$26 MagicNumber:ReplayCache.kt$ReplayCache$1000 @@ -91,6 +92,7 @@ MagicNumber:ScreenshotRecorder.kt$ScreenshotRecorderConfig.Companion$8 MagicNumber:SimpleVideoEncoder.kt$MuxerConfig$24 MagicNumber:SimpleVideoEncoder.kt$SimpleVideoEncoder$26 + MagicNumber:SimpleVideoEncoder.kt$SimpleVideoEncoder$6 MagicNumber:ViewHierarchyNode.kt$ViewHierarchyNode$26 MagicNumber:Views.kt$10 MagicNumber:Views.kt$21