From 98cfd05d79279fdde2d53ca296830edbd7e1c7d7 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Wed, 17 Dec 2025 17:58:27 +0100 Subject: [PATCH 1/2] Add jcmp plugin to ease up spotting api differences for muzzle --- build.gradle.kts | 23 ++++++++++++++++++++ docs/how_instrumentations_work.md | 36 ++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cbda3f76f82..8fa3a9c93ee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,6 +12,7 @@ plugins { id("dd-trace-java.ci-jobs") id("com.diffplug.spotless") version "8.1.0" + id("me.champeau.gradle.japicmp") version "0.4.3" id("com.github.spotbugs") version "6.4.7" id("de.thetaphi.forbiddenapis") version "3.10" id("io.github.gradle-nexus.publish-plugin") version "2.0.0" @@ -156,3 +157,25 @@ testAggregate( ":dd-java-agent:agent-debugger" ) ) + +// JApiCmp configuration example +// Usage: ./gradlew japicmp -Partifact=groupId:artifactId -Pbaseline=1.0.0 -Ptarget=2.0.0 +tasks.register("japicmp") { + val artifact = providers.gradleProperty("artifact").orNull + val baseline = providers.gradleProperty("baseline").orNull + val target = providers.gradleProperty("target").orNull + + if (artifact != null && baseline != null && target != null) { + oldClasspath.from(configurations.detachedConfiguration( + dependencies.create("$artifact:$baseline") + )) + newClasspath.from(configurations.detachedConfiguration( + dependencies.create("$artifact:$target") + )) + onlyModified.set(true) + failOnModification.set(false) + ignoreMissingClasses.set(true) + txtOutputFile.set(layout.buildDirectory.file("reports/japicmp.txt")) + htmlOutputFile.set(layout.buildDirectory.file("reports/japicmp.html")) + } +} diff --git a/docs/how_instrumentations_work.md b/docs/how_instrumentations_work.md index ec641ccf928..21a729ed64b 100644 --- a/docs/how_instrumentations_work.md +++ b/docs/how_instrumentations_work.md @@ -91,14 +91,44 @@ To run muzzle on your instrumentation, run: > [!WARNING] > Muzzle does _not_ run tests. -> It checks that the types and methods used by the instrumentation are present in particular versions of libraries. -> It can be subverted with `MethodHandle` and reflection -- in other words, having the `muzzle` task passing is not enough +> It checks that the types and methods used by the instrumentation are present in particular versions of libraries. +> It can be subverted with `MethodHandle` and reflection -- in other words, having the `muzzle` task passing is not enough > to validate an instrumentation. By default, all the muzzle directives are checked against all the instrumentations included in a module. -However, there can be situations in which it’s only needed to check one specific directive on an instrumentation. +However, there can be situations in which it's only needed to check one specific directive on an instrumentation. At this point the instrumentation should override the method `muzzleDirective()` by returning the name of the directive to execute. +### Identifying Breaking Changes with JApiCmp + +Before defining muzzle version ranges, you can use the JApiCmp plugin to compare different versions of a library and +identify breaking API changes. This helps determine where to split version ranges in your muzzle directives. + +The `japicmp` task compares two versions of a Maven artifact and reports: +- Removed classes and methods (breaking changes) +- Added classes and methods (non-breaking changes) +- Modified methods with binary compatibility status + +#### Usage + +Compare two versions of any Maven artifact: + +```shell +./gradlew japicmp -Partifact=groupId:artifactId -Pbaseline=oldVersion -Ptarget=newVersion +``` + +For example, to compare MongoDB driver versions: + +```shell +./gradlew japicmp -Partifact=org.mongodb:mongodb-driver-sync -Pbaseline=3.11.0 -Ptarget=4.0.0 +``` + +#### Output + +The task generates two reports: + +- **Text report**: `build/reports/japicmp.txt` - Detailed line-by-line comparison +- **HTML report**: `build/reports/japicmp.html` - Browsable visual report ## Instrumentation classes From 3b5371c1e4a3da54056d62839484705f706891ea Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Thu, 18 Dec 2025 10:35:38 +0100 Subject: [PATCH 2/2] spotless --- build.gradle.kts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8fa3a9c93ee..b1a691f2a50 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -166,12 +166,16 @@ tasks.register("japicmp") { val target = providers.gradleProperty("target").orNull if (artifact != null && baseline != null && target != null) { - oldClasspath.from(configurations.detachedConfiguration( - dependencies.create("$artifact:$baseline") - )) - newClasspath.from(configurations.detachedConfiguration( - dependencies.create("$artifact:$target") - )) + oldClasspath.from( + configurations.detachedConfiguration( + dependencies.create("$artifact:$baseline") + ) + ) + newClasspath.from( + configurations.detachedConfiguration( + dependencies.create("$artifact:$target") + ) + ) onlyModified.set(true) failOnModification.set(false) ignoreMissingClasses.set(true)