From 96cafa08041431d68e73d500f0e4f569f15fa98e Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Wed, 16 Sep 2026 20:56:55 +0000 Subject: [PATCH 1/2] Improve Muzzle version range failure diagnostics --- .../plugin/muzzle/MuzzleMavenRepoUtils.kt | 32 +++++++++++ .../plugin/muzzle/MuzzleMavenRepoUtilsTest.kt | 55 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt index a35d00f6c63..71887f44be9 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt @@ -155,15 +155,22 @@ internal object MuzzleMavenRepoUtils { var attemptCount = 0 var range: VersionRangeResult? = null var failure: VersionRangeResolutionException? = null + val resultExceptions = mutableListOf>>() fun attemptResolve(): VersionRangeResult? { attemptCount++ return try { range = system.resolveVersionRange(session, rangeRequest) failure = null + range?.exceptions?.takeIf { it.isNotEmpty() }?.let { exceptions -> + resultExceptions += attemptCount to exceptions.toList() + } range?.takeIf { it.hasBounds() } } catch (e: VersionRangeResolutionException) { failure = e range = e.result ?: range + e.result?.exceptions?.takeIf { it.isNotEmpty() }?.let { exceptions -> + resultExceptions += attemptCount to exceptions.toList() + } null } } @@ -195,6 +202,7 @@ internal object MuzzleMavenRepoUtils { rangeRequest.repositories, range, failure, + resultExceptions, attemptCount, waitedSeconds, enableBackoffRetries @@ -275,6 +283,7 @@ internal object MuzzleMavenRepoUtils { repositories: List, range: VersionRangeResult?, failure: VersionRangeResolutionException?, + resultExceptions: List>>, attemptCount: Int, waitedSeconds: Long, enableBackoffRetries: Boolean @@ -303,6 +312,15 @@ internal object MuzzleMavenRepoUtils { appendLine(" highestVersion=${range.highestVersion ?: ""}") appendLine(" versionCount=${range.versions.size}") } + if (resultExceptions.isNotEmpty()) { + appendLine("Resolution result exceptions:") + resultExceptions.forEach { (attempt, exceptions) -> + appendLine(" Attempt $attempt:") + exceptions.forEach { exception -> + appendException(exception, " ") + } + } + } if (failure != null) { appendLine("Last resolution failure:") appendLine(" ${failure.javaClass.name}: ${failure.message ?: ""}") @@ -313,6 +331,20 @@ internal object MuzzleMavenRepoUtils { }.trimEnd() } + private fun StringBuilder.appendException(exception: Throwable, indent: String) { + var current: Throwable? = exception + var currentIndent = indent + var depth = 0 + while (current != null && depth < 10) { + val prefix = if (depth == 0) "" else "Caused by: " + appendLine("$currentIndent$prefix${current.javaClass.name}: ${current.message ?: ""}") + val cause = current.cause + current = cause?.takeUnless { it === current } + currentIndent += " " + depth++ + } + } + private fun artifactCoordinates(artifact: Artifact): String { val classifier = artifact.classifier?.takeUnless { it.isEmpty() } return listOfNotNull( diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt index 682b8e29f49..45dbef534ca 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt @@ -17,6 +17,7 @@ import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource import java.io.File +import java.io.IOException import java.lang.reflect.Proxy import java.util.concurrent.atomic.AtomicInteger import org.assertj.core.api.Assertions.assertThat @@ -171,6 +172,35 @@ class MuzzleMavenRepoUtilsTest { assertThat(attempts).hasValue(4) } + @Test + fun `resolveVersionRange failure includes embedded result exceptions from every attempt`() { + val directive = MuzzleDirective().apply { + group = "com.example" + module = "mylib" + versions = "[1.0,)" + } + val attempts = AtomicInteger() + val failingSystem = repositorySystemReturningEmptyResultsWithExceptions(attempts) + + assertThatThrownBy { + MuzzleMavenRepoUtils.resolveVersionRange( + directive, + failingSystem, + newSession(), + emptyList(), + enableBackoffRetries = false + ) + }.isInstanceOf(IllegalStateException::class.java) + .hasMessageContaining("Resolution result exceptions:") + .hasMessageContaining("Attempt 1:") + .hasMessageContaining("Attempt 4:") + .hasMessageContaining("java.lang.IllegalStateException: metadata failure 1") + .hasMessageContaining("Caused by: java.io.IOException: download failure 1") + .hasMessageContaining("java.lang.IllegalStateException: metadata failure 4") + .hasMessageContaining("Caused by: java.io.IOException: download failure 4") + assertThat(attempts).hasValue(4) + } + @Test fun `resolveVersionRange includes directive extra repositories`() { val repoA = publishAndGetRepo("com.example", "mylib", listOf("1.0.0", "2.0.0"), subDir = "repoA") @@ -353,4 +383,29 @@ class MuzzleMavenRepoUtilsTest { else -> throw UnsupportedOperationException(method.name) } } as RepositorySystem + + private fun repositorySystemReturningEmptyResultsWithExceptions( + attempts: AtomicInteger + ): RepositorySystem = + Proxy.newProxyInstance( + RepositorySystem::class.java.classLoader, + arrayOf(RepositorySystem::class.java) + ) { _, method, args -> + when (method.name) { + "resolveVersionRange" -> { + val attempt = attempts.incrementAndGet() + val request = args?.get(1) as VersionRangeRequest + VersionRangeResult(request).apply { + addException( + IllegalStateException( + "metadata failure $attempt", + IOException("download failure $attempt") + ) + ) + } + } + "toString" -> "repositorySystemReturningEmptyResultsWithExceptions" + else -> throw UnsupportedOperationException(method.name) + } + } as RepositorySystem } From 84de5f8837df7fda0c90834647a4aa1ba8e8c567 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Mon, 21 Sep 2026 16:45:40 +0200 Subject: [PATCH 2/2] refactor: simplify Muzzle failure diagnostics --- .../plugin/muzzle/MuzzleMavenRepoUtils.kt | 19 ++++---- .../plugin/muzzle/MuzzleMavenRepoUtilsTest.kt | 48 ++++++------------- 2 files changed, 23 insertions(+), 44 deletions(-) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt index 71887f44be9..c5bb333da6f 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtils.kt @@ -158,21 +158,18 @@ internal object MuzzleMavenRepoUtils { val resultExceptions = mutableListOf>>() fun attemptResolve(): VersionRangeResult? { attemptCount++ - return try { - range = system.resolveVersionRange(session, rangeRequest) + val result = try { failure = null - range?.exceptions?.takeIf { it.isNotEmpty() }?.let { exceptions -> - resultExceptions += attemptCount to exceptions.toList() - } - range?.takeIf { it.hasBounds() } + system.resolveVersionRange(session, rangeRequest) } catch (e: VersionRangeResolutionException) { failure = e - range = e.result ?: range - e.result?.exceptions?.takeIf { it.isNotEmpty() }?.let { exceptions -> - resultExceptions += attemptCount to exceptions.toList() - } - null + e.result ?: return null + } + range = result + if (result.exceptions.isNotEmpty()) { + resultExceptions += attemptCount to result.exceptions.toList() } + return result.takeIf { failure == null && it.hasBounds() } } repeat(4) { diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt index 45dbef534ca..d53cc5e5d80 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleMavenRepoUtilsTest.kt @@ -72,7 +72,7 @@ class MuzzleMavenRepoUtilsTest { versions = "[1.0,)" } val attempts = AtomicInteger() - val retryingSystem = repositorySystemThrowingThenResolving( + val retryingSystem = repositorySystemReturningAfterFailures( failuresBeforeSuccess = 3, result = createVersionRangeResult("1.0.0"), attempts = attempts @@ -150,7 +150,7 @@ class MuzzleMavenRepoUtilsTest { versions = "[1.0,)" } val attempts = AtomicInteger() - val throwingSystem = repositorySystemThrowingThenResolving( + val throwingSystem = repositorySystemReturningAfterFailures( failuresBeforeSuccess = 4, result = createVersionRangeResult("1.0.0"), attempts = attempts @@ -180,7 +180,15 @@ class MuzzleMavenRepoUtilsTest { versions = "[1.0,)" } val attempts = AtomicInteger() - val failingSystem = repositorySystemReturningEmptyResultsWithExceptions(attempts) + val emptyResult = createVersionRangeResult().apply { + addException( + IllegalStateException( + "metadata failure", + IOException("download failure") + ) + ) + } + val failingSystem = repositorySystemReturningAfterFailures(0, emptyResult, attempts) assertThatThrownBy { MuzzleMavenRepoUtils.resolveVersionRange( @@ -194,10 +202,8 @@ class MuzzleMavenRepoUtilsTest { .hasMessageContaining("Resolution result exceptions:") .hasMessageContaining("Attempt 1:") .hasMessageContaining("Attempt 4:") - .hasMessageContaining("java.lang.IllegalStateException: metadata failure 1") - .hasMessageContaining("Caused by: java.io.IOException: download failure 1") - .hasMessageContaining("java.lang.IllegalStateException: metadata failure 4") - .hasMessageContaining("Caused by: java.io.IOException: download failure 4") + .hasMessageContaining("java.lang.IllegalStateException: metadata failure") + .hasMessageContaining("Caused by: java.io.IOException: download failure") assertThat(attempts).hasValue(4) } @@ -358,7 +364,7 @@ class MuzzleMavenRepoUtilsTest { return VersionRangeResult(request).apply { this.versions = versions } } - private fun repositorySystemThrowingThenResolving( + private fun repositorySystemReturningAfterFailures( failuresBeforeSuccess: Int, result: VersionRangeResult, attempts: AtomicInteger @@ -379,33 +385,9 @@ class MuzzleMavenRepoUtilsTest { } result } - "toString" -> "repositorySystemThrowingThenResolving" + "toString" -> "repositorySystemReturningAfterFailures" else -> throw UnsupportedOperationException(method.name) } } as RepositorySystem - private fun repositorySystemReturningEmptyResultsWithExceptions( - attempts: AtomicInteger - ): RepositorySystem = - Proxy.newProxyInstance( - RepositorySystem::class.java.classLoader, - arrayOf(RepositorySystem::class.java) - ) { _, method, args -> - when (method.name) { - "resolveVersionRange" -> { - val attempt = attempts.incrementAndGet() - val request = args?.get(1) as VersionRangeRequest - VersionRangeResult(request).apply { - addException( - IllegalStateException( - "metadata failure $attempt", - IOException("download failure $attempt") - ) - ) - } - } - "toString" -> "repositorySystemReturningEmptyResultsWithExceptions" - else -> throw UnsupportedOperationException(method.name) - } - } as RepositorySystem }