Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -156,3 +157,29 @@ 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<me.champeau.gradle.japicmp.JapicmpTask>("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"))
}
}
36 changes: 33 additions & 3 deletions docs/how_instrumentations_work.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 its 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

Expand Down