From ca7c616e301d83159dbdc7d0b31334754d639d30 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Tue, 6 Oct 2026 20:14:07 +0200 Subject: [PATCH 1/4] refactor: aggregate muzzle reports through variants --- build.gradle.kts | 6 + buildSrc/build.gradle.kts | 4 + .../gradle/plugin/muzzle/MuzzlePlugin.kt | 10 +- .../MuzzleReportAggregationExtension.kt | 7 + .../muzzle/MuzzleReportAggregationPlugin.kt | 55 +++ .../plugin/muzzle/MuzzleReportVariants.kt | 24 ++ .../muzzle/tasks/AbstractMuzzleReportTask.kt | 9 +- ...tsTask.kt => MuzzleAggregateReportTask.kt} | 18 +- .../muzzle/tasks/MuzzleGenerateReportTask.kt | 25 +- .../muzzle/MuzzleReportAggregationTest.kt | 394 ++++++++++++++++++ .../MuzzleReportConfigurationCacheTest.kt | 125 +++--- dd-java-agent/instrumentation/build.gradle | 19 +- docs/how_instrumentations_work.md | 28 +- 13 files changed, 628 insertions(+), 96 deletions(-) create mode 100644 buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationExtension.kt create mode 100644 buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt create mode 100644 buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportVariants.kt rename buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/{MuzzleMergeReportsTask.kt => MuzzleAggregateReportTask.kt} (70%) create mode 100644 buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 30606877d37..ea58625770e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -128,6 +128,12 @@ allprojects { tasks.register("latestDepTest") +tasks.register("generateMuzzleReport") { + group = "Muzzle" + description = "Generate the aggregate instrumentation dependency version report" + dependsOn(":dd-java-agent:instrumentation:aggregateMuzzleReports") +} + nexusPublishing { repositories { val forceLocal = providers.gradleProperty("forceLocal").getOrElse("false").toBoolean() diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 135ae0be3b9..1da7da38122 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -29,6 +29,10 @@ gradlePlugin { id = "dd-trace-java.muzzle" implementationClass = "datadog.gradle.plugin.muzzle.MuzzlePlugin" } + create("muzzle-report-aggregation-plugin") { + id = "dd-trace-java.muzzle-report-aggregation" + implementationClass = "datadog.gradle.plugin.muzzle.MuzzleReportAggregationPlugin" + } create("call-site-instrumentation-plugin") { id = "dd-trace-java.call-site-instrumentation" implementationClass = "datadog.gradle.plugin.csi.CallSiteInstrumentationPlugin" diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt index 3e3217d2f90..269069f354d 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt @@ -3,7 +3,6 @@ package datadog.gradle.plugin.muzzle import datadog.gradle.plugin.muzzle.tasks.MuzzleEndTask import datadog.gradle.plugin.muzzle.tasks.MuzzleGenerateReportTask import datadog.gradle.plugin.muzzle.tasks.MuzzleGetReferencesTask -import datadog.gradle.plugin.muzzle.tasks.MuzzleMergeReportsTask import datadog.gradle.plugin.muzzle.tasks.MuzzleTask import datadog.gradle.plugin.muzzle.planner.MuzzleTaskPlanner import org.eclipse.aether.artifact.Artifact @@ -92,11 +91,14 @@ class MuzzlePlugin : Plugin { it.configure { finalizedBy(printReferencesTask) } } - project.tasks.register("generateMuzzleReport") { + val extension = project.extensions.getByType() + val runtimeClasspath = project.mainSourceSet.runtimeClasspath + val report = project.tasks.register("generateMuzzleReport") { + reportDirectives.convention(project.providers.provider { extension.directives }) + instrumentationClasspath.from(reportingDirectives.map { if (it.isEmpty()) emptyList() else runtimeClasspath }) dependsOn(compileMuzzle) } - - project.tasks.register("mergeMuzzleReports") + project.publishMuzzleReport(report.flatMap { it.versionsFile }) val hasRelevantTask = project.gradle.startParameter.taskNames.any { taskName -> val taskProjectPath = taskName.substringBeforeLast(":", "") diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationExtension.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationExtension.kt new file mode 100644 index 00000000000..0421e5f660e --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationExtension.kt @@ -0,0 +1,7 @@ +package datadog.gradle.plugin.muzzle + +import org.gradle.api.file.RegularFileProperty + +abstract class MuzzleReportAggregationExtension { + abstract val reportFile: RegularFileProperty +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt new file mode 100644 index 00000000000..d71cc2d76af --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt @@ -0,0 +1,55 @@ +package datadog.gradle.plugin.muzzle + +import datadog.gradle.plugin.muzzle.tasks.MuzzleAggregateReportTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.Usage +import org.gradle.api.plugins.JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.register + +/** Aggregates dependency reports published by instrumentation projects. */ +class MuzzleReportAggregationPlugin : Plugin { + override fun apply(project: Project) { + val extension = project.extensions.create("muzzleReports") + extension.reportFile.convention(project.layout.buildDirectory.file("muzzle-deps-results/muzzle.csv")) + + val aggregation = project.configurations.dependencyScope("muzzleReportAggregation") + // Reuse the Java project's declared dependencies instead of inspecting its subprojects. + project.pluginManager.withPlugin("java") { + aggregation.configure { + extendsFrom(project.configurations.getByName(IMPLEMENTATION_CONFIGURATION_NAME)) + } + } + + val results = project.configurations.resolvable("aggregateMuzzleReportResults") { + extendsFrom(aggregation.get()) + isTransitive = false + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category::class.java, Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) + } + } + + val reports = results.map { configuration -> + configuration.incoming.artifactView { + // Select reports from the Java graph; plain Java stubs have no matching report variant. + withVariantReselection() + attributes.muzzleReportAttributes(project.objects) + }.files + } + + val report = project.tasks.register("aggregateMuzzleReports") { + versionReports.from(reports) + versionsFile.convention(extension.reportFile) + } + + project.tasks.register("mergeMuzzleReports") { + group = "Muzzle" + description = "Deprecated: use aggregateMuzzleReports for the aggregate dependency report" + dependsOn(report) + doFirst { logger.warn("mergeMuzzleReports is deprecated; use aggregateMuzzleReports") } + } + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportVariants.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportVariants.kt new file mode 100644 index 00000000000..d93a59f2f9c --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportVariants.kt @@ -0,0 +1,24 @@ +package datadog.gradle.plugin.muzzle + +import org.gradle.api.Project +import org.gradle.api.attributes.AttributeContainer +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.VerificationType +import org.gradle.api.file.RegularFile +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Provider + +private const val MUZZLE_REPORT_TYPE = "muzzle-dependency-report" + +internal fun AttributeContainer.muzzleReportAttributes(objects: ObjectFactory) { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.VERIFICATION)) + attribute(VerificationType.VERIFICATION_TYPE_ATTRIBUTE, objects.named(VerificationType::class.java, MUZZLE_REPORT_TYPE)) +} + +/** Publishes the producer's output with its inferred task dependency. */ +fun Project.publishMuzzleReport(report: Provider) { + configurations.consumable("muzzleReportElements") { + attributes.muzzleReportAttributes(objects) + outgoing.artifact(report) + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/AbstractMuzzleReportTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/AbstractMuzzleReportTask.kt index 65ba025e11f..9f88a5c1588 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/AbstractMuzzleReportTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/AbstractMuzzleReportTask.kt @@ -1,18 +1,13 @@ package datadog.gradle.plugin.muzzle.tasks import datadog.gradle.plugin.muzzle.TestedArtifact -import datadog.gradle.plugin.muzzle.pathSlug -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Provider +import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.OutputFile import java.util.SortedMap abstract class AbstractMuzzleReportTask : AbstractMuzzleTask() { @get:OutputFile - val versionsFile: Provider = project.rootProject - .layout - .buildDirectory - .file("$MUZZLE_DEPS_RESULTS/${project.pathSlug}.csv") + abstract val versionsFile: RegularFileProperty internal fun dumpVersionsToCsv(versions: SortedMap) { val file = versionsFile.get().asFile diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleMergeReportsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleAggregateReportTask.kt similarity index 70% rename from buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleMergeReportsTask.kt rename to buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleAggregateReportTask.kt index 93ec54e45af..95b8558a49d 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleMergeReportsTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleAggregateReportTask.kt @@ -3,27 +3,27 @@ package datadog.gradle.plugin.muzzle.tasks import datadog.gradle.plugin.muzzle.MuzzleMavenRepoUtils import datadog.gradle.plugin.muzzle.TestedArtifact import org.eclipse.aether.util.version.GenericVersionScheme +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import java.util.TreeMap -abstract class MuzzleMergeReportsTask : AbstractMuzzleReportTask() { +abstract class MuzzleAggregateReportTask : AbstractMuzzleReportTask() { init { - description = "Merge generated dependency version reports into one CSV" + description = "Aggregate instrumentation dependency version reports" + versionsFile.convention(project.layout.buildDirectory.file("$MUZZLE_DEPS_RESULTS/muzzle.csv")) + // Preserve the existing order-sensitive handling of Maven-equivalent version spellings. + outputs.upToDateWhen { false } } @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) - val versionReports = project.files( - project.rootProject.allprojects.flatMap { producer -> - producer.tasks.withType(MuzzleGenerateReportTask::class.java).map { it.versionsFile } - } - ).minus(project.files(versionsFile)).asFileTree + @get:PathSensitive(PathSensitivity.NONE) + abstract val versionReports: ConfigurableFileCollection /** - * Merges existing CSVs from current report generators and writes the merged results to a CSV. + * Merges the declared muzzle report artifacts into a single CSV. */ @TaskAction fun mergeReports() { diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt index c98992bed07..361f3449bca 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt @@ -1,21 +1,21 @@ package datadog.gradle.plugin.muzzle.tasks import datadog.gradle.plugin.muzzle.MuzzleDirective -import datadog.gradle.plugin.muzzle.MuzzleExtension import datadog.gradle.plugin.muzzle.MuzzleMavenRepoUtils import datadog.gradle.plugin.muzzle.MuzzleMavenRepoUtils.highest import datadog.gradle.plugin.muzzle.MuzzleMavenRepoUtils.lowest import datadog.gradle.plugin.muzzle.MuzzleMavenRepoUtils.resolveInstrumentationAndJarVersions import datadog.gradle.plugin.muzzle.TestedArtifact -import datadog.gradle.plugin.muzzle.mainSourceSet +import datadog.gradle.plugin.muzzle.pathSlug import org.eclipse.aether.RepositorySystem import org.eclipse.aether.RepositorySystemSession import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.getByType import java.net.URL import java.net.URLClassLoader import java.util.TreeMap @@ -28,20 +28,17 @@ abstract class MuzzleGenerateReportTask : AbstractMuzzleReportTask() { @get:Classpath abstract val instrumentationClasspath: ConfigurableFileCollection + @get:Internal + val reportingDirectives: Provider> = reportDirectives.map { directives -> + directives.filter { !it.isCoreJdk && !it.skipFromReport } + } + init { description = "Generate this instrumentation's dependency version report" - + reportDirectives.convention(emptyList()) + versionsFile.convention(project.layout.buildDirectory.file("$MUZZLE_DEPS_RESULTS/${project.pathSlug}.csv")) // Repository metadata can change without any local task input changing. outputs.upToDateWhen { false } - - val extension = project.extensions.getByType() - val runtimeClasspath = project.mainSourceSet.runtimeClasspath - val directives = project.providers.provider { - extension.directives.filter { !it.isCoreJdk && !it.skipFromReport } - } - - reportDirectives.set(directives) - instrumentationClasspath.from(directives.map { if (it.isEmpty()) emptyList() else runtimeClasspath }) } @TaskAction @@ -49,7 +46,7 @@ abstract class MuzzleGenerateReportTask : AbstractMuzzleReportTask() { val system: RepositorySystem = MuzzleMavenRepoUtils.newRepositorySystem() val session: RepositorySystemSession = MuzzleMavenRepoUtils.newRepositorySystemSession(system) val versions = TreeMap() - reportDirectives.get().forEach { directive -> + reportingDirectives.get().forEach { directive -> val range = MuzzleMavenRepoUtils.resolveVersionRange(directive, system, session) val cp = instrumentationClasspath.map { it.toURI().toURL() }.toTypedArray() val partials = URLClassLoader(cp, null).use { cl -> diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt new file mode 100644 index 00000000000..04dd239a039 --- /dev/null +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt @@ -0,0 +1,394 @@ +package datadog.gradle.plugin.muzzle + +import org.assertj.core.api.Assertions.assertThat +import org.gradle.testkit.runner.TaskOutcome.FAILED +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE +import org.junit.jupiter.api.Test + +class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { + @Test + fun `aggregates only published reports and refreshes Maven ranges with configuration cache reuse`() { + val repo = createMavenRepoFixture() + repo.publishVersions("example", "library", listOf("1.0", "1.10", "2.0", "3.0")) + writeReportingProject( + """ + muzzle { + pass { + name = "shared" + group = "example" + module = "library" + versions = "[1,2)" + } + fail { + name = "shared" + group = "example" + module = "library" + versions = "[2,3)" + } + pass { coreJdk() } + pass { + group = "missing" + module = "skipped" + versions = "[1,)" + skipFromReport = true + } + } + """ + ) + addAggregationInput("dd-java-agent:instrumentation:nested:other", producerScript( + """ + muzzle { + pass { + name = "shared" + group = "example" + module = "library" + versions = "[3,)" + } + pass { + name = "alpha" + group = "example" + module = "library" + versions = "[1,2)" + } + } + """ + )) + addAggregationInput("dd-java-agent:instrumentation:empty", producerScript("muzzle { pass { coreJdk() } }")) + addAggregationInput("dd-java-agent:instrumentation:stubs", """plugins { id("java") }""") + addSubproject("dd-java-agent:instrumentation:unselected", producerScript("muzzle { pass { coreJdk() } }")) + writeFile("relocated/build/muzzle-deps-results/stale.csv", HEADER + "stale,example,library,0,99") + writeFile(REPORT, HEADER + "previous,example,library,0,99") + + val args = arrayOf(":generateMuzzleReport", "--configuration-cache", "--configuration-cache-problems=fail", "--build-cache", "--stacktrace") + val env = mapOf("MAVEN_REPOSITORY_PROXY" to repo.repoUrl) + val first = run(*args, env = env) + assertThat(first.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(first.output).isEqualTo(SUCCESS) + assertThat(first.output).contains("Configuration cache entry stored") + assertThat(file(REPORT).readText()).isEqualTo( + HEADER + "alpha,example,library,1.0,1.10\nshared,example,library,1.0,3.0\n" + ) + assertThat(file("dd-java-agent/instrumentation/demo/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv").readText()) + .isEqualTo(HEADER + "shared,example,library,1.0,2.0\n") + assertThat(file("dd-java-agent/instrumentation/empty/build/muzzle-deps-results/dd-java-agent_instrumentation_empty.csv").readText()) + .isEqualTo(HEADER) + assertThat(first.tasks).noneMatch { it.path.contains("muzzle-Assert") } + assertThat(first.task(":dd-java-agent:instrumentation:demo:compileMuzzle")).isNotNull() + assertThat(first.tasks).noneMatch { it.path.startsWith(":dd-java-agent:instrumentation:stubs:") } + assertThat(first.tasks).noneMatch { it.path.startsWith(":dd-java-agent:instrumentation:unselected:") } + assertThat(first.tasks).noneMatch { it.path == ":aggregateMuzzleReports" || it.path.endsWith(":muzzleInstrumentationReport") } + + val second = run(*args, env = env) + assertThat(second.output).contains("Reusing configuration cache") + assertThat(second.task(":dd-java-agent:instrumentation:demo:generateMuzzleReport")?.outcome).isEqualTo(SUCCESS) + assertThat(second.task(":dd-java-agent:instrumentation:nested:other:generateMuzzleReport")?.outcome).isEqualTo(SUCCESS) + assertThat(second.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).isEqualTo(SUCCESS) + assertThat(second.task(":generateMuzzleReport")?.outcome).isIn(SUCCESS, UP_TO_DATE) + + repo.publishVersions("example", "library", listOf("4.0")) + val refreshed = run(*args, env = env) + assertThat(refreshed.output).contains("Reusing configuration cache") + assertThat(refreshed.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(refreshed.output).isEqualTo(SUCCESS) + assertThat(file(REPORT).readText()).isEqualTo( + HEADER + "alpha,example,library,1.0,1.10\nshared,example,library,1.0,4.0\n" + ) + } + + @Test + fun `help does not query Maven and producer failure prevents aggregation`() { + val repo = createMavenRepoFixture() + writeReportingProject( + """ + muzzle { + pass { + group = "missing" + module = "library" + versions = "[1,)" + } + } + """ + ) + val env = mapOf("MAVEN_REPOSITORY_PROXY" to repo.repoUrl) + val help = run("help", "--configuration-cache", "--configuration-cache-problems=fail", env = env) + assertThat(help.task(":help")?.outcome).describedAs(help.output).isEqualTo(SUCCESS) + assertThat(help.tasks).noneMatch { it.path.contains("Muzzle") || it.path.contains("muzzle") } + assertThat(file(REPORT)).doesNotExist() + + val failed = run(":generateMuzzleReport", "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace", expectFailure = true, env = env) + assertThat(failed.output).contains("BUILD FAILED") + assertThat(failed.task(":dd-java-agent:instrumentation:demo:generateMuzzleReport")?.outcome).isEqualTo(FAILED) + assertThat(failed.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")).isNull() + assertThat(file(REPORT)).doesNotExist() + } + + @Test + fun `empty aggregation ignores non reporting projects and emits the CSV header`() { + writeSettings("""rootProject.name = "empty-reports"""") + writeAggregationProject() + addAggregationInput("dd-java-agent:instrumentation:stubs", """plugins { id("java") }""") + val result = run("aggregateMuzzleReports", "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace") + assertThat(result.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(result.output).isEqualTo(SUCCESS) + assertThat(file(REPORT).readText()).isEqualTo(HEADER) + } + + @Test + fun `Maven equivalent versions retain the existing last spelling selection`() { + writeSettings("""rootProject.name = "equivalent-versions"""") + writeAggregationProject() + writeFile("dd-java-agent/instrumentation/build.gradle.kts", + """ + tasks.named("aggregateMuzzleReports") { + versionReports.from(layout.projectDirectory.file("versions.csv")) + } + """, append = true + ) + val a = "same,example,library,1,2\n" + val b = "same,example,library,1.0,2.0\n" + listOf(a + b, b + a).forEach { rows -> + writeFile("dd-java-agent/instrumentation/versions.csv", HEADER + rows) + val result = run("aggregateMuzzleReports", "--configuration-cache", "--configuration-cache-problems=fail") + assertThat(result.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(result.output).isEqualTo(SUCCESS) + val expected = if (rows == a + b) "same,example,library,1.0,2.0\n" else "same,example,library,1,2\n" + assertThat(file(REPORT).readText()).isEqualTo(HEADER + expected) + } + } + + @Test + fun `module generation stays local even when aggregation is configured`() { + val repo = createMavenRepoFixture() + repo.publishVersions("example", "library", listOf("1.0", "2.0")) + writeReportingProject( + """ + muzzle { + pass { + name = "demo" + group = "example" + module = "library" + versions = "[1,2)" + } + } + """ + ) + addAggregationInput("dd-java-agent:instrumentation:other", producerScript( + """ + muzzle { + pass { + group = "missing" + module = "library" + versions = "[1,)" + } + } + """ + )) + val args = arrayOf( + ":dd-java-agent:instrumentation:demo:generateMuzzleReport", + "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace" + ) + val env = mapOf("MAVEN_REPOSITORY_PROXY" to repo.repoUrl) + val first = run(*args, env = env) + assertThat(first.task(args[0])?.outcome).describedAs(first.output).isEqualTo(SUCCESS) + assertThat(first.output).contains("Configuration cache entry stored") + assertThat(first.output).doesNotContain("generateMuzzleReport is deprecated") + assertThat(first.task(":dd-java-agent:instrumentation:demo:compileMuzzle")).isNotNull() + assertThat(first.task(":dd-java-agent:agent-bootstrap:compileMain_java11Java")).isNotNull() + assertThat(first.task(":dd-java-agent:instrumentation:other:generateMuzzleReport")).isNull() + assertThat(first.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")).isNull() + assertThat(file(REPORT)).doesNotExist() + assertThat(file("dd-java-agent/instrumentation/relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv")) + .doesNotExist() + val expected = HEADER + "demo,example,library,1.0,1.0\n" + assertThat(file("dd-java-agent/instrumentation/demo/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv").readText()) + .isEqualTo(expected) + val reused = run(*args, env = env) + assertThat(reused.task(args[0])?.outcome).describedAs(reused.output).isEqualTo(SUCCESS) + assertThat(reused.output).contains("Reusing configuration cache") + assertThat(file("dd-java-agent/instrumentation/demo/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv").readText()) + .isEqualTo(expected) + assertThat(file(REPORT)).doesNotExist() + } + + @Test + fun `parent merge command delegates to aggregation`() { + writeReportingProject("muzzle { pass { coreJdk() } }") + val result = run( + ":dd-java-agent:instrumentation:mergeMuzzleReports", + "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace" + ) + assertThat(result.task(":dd-java-agent:instrumentation:mergeMuzzleReports")?.outcome).describedAs(result.output).isEqualTo(SUCCESS) + assertThat(result.task(":aggregateMuzzleReports")).isNull() + assertThat(result.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).isEqualTo(SUCCESS) + assertThat(result.task(":dd-java-agent:instrumentation:demo:generateMuzzleReport")?.outcome).isEqualTo(SUCCESS) + assertThat(result.output).contains("mergeMuzzleReports is deprecated; use aggregateMuzzleReports") + assertThat(file(REPORT).readText()).isEqualTo(HEADER) + assertThat(file("dd-java-agent/instrumentation/relocated/build/muzzle-deps-results/legacy-parent.csv")) + .doesNotExist() + } + + @Test + fun `changing report artifact order retains legacy version spelling without reusing task output`() { + writeSettings("""rootProject.name = "ordered-reports"""") + writeAggregationProject() + writeFile("dd-java-agent/instrumentation/a.csv", HEADER + "same,example,library,1,2") + writeFile("dd-java-agent/instrumentation/b.csv", HEADER + "same,example,library,1.0,2.0") + writeFile("dd-java-agent/instrumentation/build.gradle.kts", + """ + tasks.named("aggregateMuzzleReports") { + val reports = if (providers.gradleProperty("reverseReports").isPresent) listOf("b.csv", "a.csv") else listOf("a.csv", "b.csv") + versionReports.setFrom(reports.map { layout.projectDirectory.file(it) }) + } + """, append = true + ) + val args = arrayOf("aggregateMuzzleReports", "--configuration-cache", "--configuration-cache-problems=fail", "--build-cache") + val first = run(*args) + assertThat(first.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(first.output).isEqualTo(SUCCESS) + assertThat(file(REPORT).readText()).isEqualTo(HEADER + "same,example,library,1.0,2.0\n") + val reversed = run(*args, "-PreverseReports") + assertThat(reversed.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(reversed.output).isEqualTo(SUCCESS) + assertThat(file(REPORT).readText()).isEqualTo(HEADER + "same,example,library,1,2\n") + } + + @Test + fun `task directive overrides determine filtering`() { + writeReportingProject( + """ + muzzle { + pass { group = "missing"; module = "library"; versions = "[1,)" } + } + tasks.named("generateMuzzleReport") { + reportDirectives.set(emptyList()) + } + """ + ) + val result = run( + ":dd-java-agent:instrumentation:demo:generateMuzzleReport", + "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace" + ) + assertThat(result.task(":dd-java-agent:instrumentation:demo:generateMuzzleReport")?.outcome) + .describedAs(result.output).isEqualTo(SUCCESS) + assertThat(result.task(":dd-java-agent:instrumentation:demo:compileMuzzle")).isNotNull() + assertThat(file("dd-java-agent/instrumentation/demo/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv").readText()) + .isEqualTo(HEADER) + } + + @Test + fun `aggregate aliases delegate to instrumentation with the relocated CI artifact path`() { + writeReportingProject("muzzle { pass { coreJdk() } }") + val cc = arrayOf("--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace") + val ci = run("generateMuzzleReport", "muzzleInstrumentationReport", *cc) + assertThat(ci.task(":generateMuzzleReport")?.outcome).describedAs(ci.output).isIn(SUCCESS, UP_TO_DATE) + assertThat(ci.task(":dd-java-agent:instrumentation:muzzleInstrumentationReport")?.outcome).isIn(SUCCESS, UP_TO_DATE) + assertThat(file(REPORT).readText()).isEqualTo(HEADER) + listOf("mergeMuzzleReports", "muzzleInstrumentationReport").forEach { alias -> + val taskPath = ":dd-java-agent:instrumentation:$alias" + val first = run(taskPath, *cc) + assertThat(first.task(taskPath)?.outcome).describedAs(first.output).isIn(SUCCESS, UP_TO_DATE) + assertThat(first.task(":dd-java-agent:instrumentation:demo:generateMuzzleReport")?.outcome).isEqualTo(SUCCESS) + val reused = run(taskPath, *cc) + assertThat(reused.output).contains("Reusing configuration cache") + assertThat(reused.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).isEqualTo(SUCCESS) + assertThat(reused.task(":aggregateMuzzleReports")).isNull() + } + } + + @Test + fun `aggregation wiring supports isolated projects without the existing validation plugin`() { + writeSettings("""rootProject.name = "isolated-reports"""") + writeAggregationProject() + addAggregationInput("dd-java-agent:instrumentation:stubs", """plugins { id("java") }""") + addAggregationInput("dd-java-agent:instrumentation:isolated", + """ + import datadog.gradle.plugin.muzzle.tasks.MuzzleGenerateReportTask + import org.gradle.api.attributes.Category + import org.gradle.api.attributes.VerificationType + + plugins { id("java") } + val report = tasks.register("report") + configurations.consumable("muzzleReportElements") { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.VERIFICATION)) + attribute(VerificationType.VERIFICATION_TYPE_ATTRIBUTE, objects.named(VerificationType::class.java, "muzzle-dependency-report")) + } + outgoing.artifact(report.flatMap { it.versionsFile }) + } + """ + ) + val args = arrayOf(":generateMuzzleReport", "--isolated-projects", "--configuration-cache-problems=fail", "--stacktrace") + val first = run(*args) + assertThat(first.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(first.output).isEqualTo(SUCCESS) + assertThat(first.output).contains("Configuration cache entry stored") + assertThat(file(REPORT).readText()).isEqualTo(HEADER) + val reused = run(*args) + assertThat(reused.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(reused.output).isEqualTo(SUCCESS) + assertThat(reused.task(":generateMuzzleReport")?.outcome).describedAs(reused.output).isIn(SUCCESS, UP_TO_DATE) + assertThat(reused.output).contains("Reusing configuration cache") + } + + private fun producerScript(directives: String) = """ + plugins { + id("java") + id("dd-trace-java.muzzle") + } + dependencies { + implementation(project(":dd-java-agent:agent-tooling")) + } + $directives + """.trimIndent() + + private fun writeReportingProject(directives: String) { + writeProject(producerScript(directives)) + writeAggregationProject() + writeFile("dd-java-agent/instrumentation/build.gradle.kts", + """dependencies { implementation(project(":dd-java-agent:instrumentation:demo")) }""", append = true + ) + writeJavaSource( + "datadog.trace.agent.tooling.muzzle.MuzzleVersionScanPlugin", + """ + package datadog.trace.agent.tooling.muzzle; + + public final class MuzzleVersionScanPlugin { + public static java.util.Set listInstrumentationNames(ClassLoader loader, String name) { + return java.util.Collections.singleton(name == null ? "default" : name); + } + } + """, + projectPath = "dd-java-agent:agent-tooling" + ) + } + + private fun writeAggregationProject() { + addSubproject("dd-java-agent:instrumentation", + """ + plugins { + id("java") + id("dd-trace-java.muzzle-report-aggregation") + } + layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) + muzzleReports { + reportFile.set(layout.buildDirectory.file("muzzle-deps-results/dd-java-agent_instrumentation.csv")) + } + tasks.register("muzzleInstrumentationReport") { dependsOn(tasks.named("aggregateMuzzleReports")) } + """ + ) + writeRootProject( + """ + plugins { id("dd-trace-java.muzzle-report-aggregation") apply false } + layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) + tasks.register("generateMuzzleReport") { + dependsOn(":dd-java-agent:instrumentation:aggregateMuzzleReports") + } + """ + ) + } + + private fun addAggregationInput(projectPath: String, script: String) { + addSubproject(projectPath, script) + writeFile("dd-java-agent/instrumentation/build.gradle.kts", + """dependencies { implementation(project(":$projectPath")) }""", append = true + ) + } + + companion object { + private const val HEADER = "instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion\n" + private const val REPORT = "dd-java-agent/instrumentation/relocated/build/muzzle-deps-results/dd-java-agent_instrumentation.csv" + } +} diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt index 8f2878bdffc..cc3184f165e 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt @@ -20,6 +20,8 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { id("dd-trace-java.muzzle") } + layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) + dependencies { runtimeOnly(project(":dd-java-agent:agent-tooling")) } @@ -47,8 +49,9 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { val first = run(*args, env = env) assertThat(first.task(args[0])?.outcome).describedAs(first.output).isEqualTo(SUCCESS) assertThat(first.output).contains("Configuration cache entry stored") + assertThat(first.tasks).noneMatch { it.path.endsWith(":aggregateMuzzleReports") || it.path.endsWith(":mergeMuzzleReports") } - val report = file("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv") + val report = file("dd-java-agent/instrumentation/demo/relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv") assertThat(report.readText()).isEqualTo( "instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion\n" + "test-instrumentation,com.example,demo,1.0.0,1.0.0\n" @@ -70,40 +73,18 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun `merge reflects changed reports with configuration cache reuse`(rerunTasks: Boolean) { - writeProject( - """ - plugins { - id("java") - id("dd-trace-java.muzzle") - } - """ - ) - addSubproject("dd-java-agent:instrumentation", - """ - plugins { - id("java") - id("dd-trace-java.muzzle") - } - """ - ) + writeProject("""plugins { id("java") }""") listOf("first", "second", "third").forEach { producer -> - addSubproject("dd-java-agent:instrumentation:$producer", - """ - plugins { - id("java") - id("dd-trace-java.muzzle") - } - """ - ) + addSubproject("dd-java-agent:instrumentation:$producer", reportProducerScript) } - writeRootProject("""layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build"))""") - writeFile("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_first.csv", + writeAggregationProject("first", "second", "third") + writeFile("dd-java-agent/instrumentation/first/versions.csv", """ instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion first-instrumentation,com.example,demo,1.0.0,2.0.0 """ ) - writeFile("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_second.csv", + writeFile("dd-java-agent/instrumentation/second/versions.csv", """ instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion first-instrumentation,com.example,demo,1.5.0,3.0.0 @@ -125,10 +106,12 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { assertThat(first.output).contains("Configuration cache entry stored") val reused = run(*args) - assertThat(reused.task(args[0])?.outcome).describedAs(reused.output).isEqualTo(UP_TO_DATE) + assertThat(reused.task(args[0])?.outcome).describedAs(reused.output).isEqualTo(SUCCESS) assertThat(reused.output).contains("Reusing configuration cache") - val report = file("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation.csv") + assertThat(reused.task(":dd-java-agent:instrumentation:first:publishReport")?.outcome).isEqualTo(UP_TO_DATE) + + val report = file("dd-java-agent/instrumentation/relocated/build/muzzle-deps-results/dd-java-agent_instrumentation.csv") assertThat(report.readText()) .isEqualTo( "instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion\n" + @@ -136,7 +119,7 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { "second-instrumentation,com.example,other,3.0.0,4.0.0\n" ) - writeFile("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_second.csv", + writeFile("dd-java-agent/instrumentation/second/versions.csv", """ instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion first-instrumentation,com.example,demo,1.5.0,2.0.0 @@ -152,7 +135,7 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { "first-instrumentation,com.example,demo,1.0.0,2.0.0\n" ) - writeFile("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_third.csv", + writeFile("dd-java-agent/instrumentation/third/versions.csv", """ instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion third-instrumentation,com.example,new,4.0.0,5.0.0 @@ -167,8 +150,8 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { "third-instrumentation,com.example,new,4.0.0,5.0.0\n" ) - assertThat(file("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_first.csv").delete()).isTrue() - assertThat(file("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_second.csv").delete()).isTrue() + assertThat(file("dd-java-agent/instrumentation/first/versions.csv").delete()).isTrue() + assertThat(file("dd-java-agent/instrumentation/second/versions.csv").delete()).isTrue() val removed = run(*refreshArgs) assertThat(removed.task(args[0])?.outcome).describedAs(removed.output).isEqualTo(SUCCESS) assertThat(removed.output).contains("Reusing configuration cache") @@ -180,25 +163,19 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { @Test fun `merge ignores reports from removed and renamed producers`() { - val producerScript = """ - plugins { - id("java") - id("dd-trace-java.muzzle") - } - """ - writeProject(producerScript) - addSubproject("dd-java-agent:instrumentation", producerScript) + writeProject(reportProducerScript) + writeAggregationProject("demo") val currentSettings = file("settings.gradle.kts").readText() - addSubproject("dd-java-agent:instrumentation:deleted", producerScript) - writeRootProject("""layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build"))""") + addSubproject("dd-java-agent:instrumentation:deleted", reportProducerScript) + writeAggregationProject("demo", "deleted") - val active = writeFile("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_demo.csv", + val active = writeFile("dd-java-agent/instrumentation/demo/versions.csv", """ instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion active-instrumentation,com.example,demo,1.0.0,2.0.0 """ ) - val orphan = writeFile("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_deleted.csv", + val orphan = writeFile("dd-java-agent/instrumentation/deleted/versions.csv", """ instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion deleted-instrumentation,com.example,deleted,3.0.0,4.0.0 @@ -210,7 +187,7 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { "--configuration-cache-problems=fail", "--stacktrace" ) - val report = file("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation.csv") + val report = file("dd-java-agent/instrumentation/relocated/build/muzzle-deps-results/dd-java-agent_instrumentation.csv") val first = run(*args) assertThat(first.task(args[0])?.outcome).describedAs(first.output).isEqualTo(SUCCESS) @@ -221,14 +198,17 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { ) writeSettings(currentSettings) + writeAggregationProject("demo") val removed = run(*args) assertThat(removed.task(args[0])?.outcome).describedAs(removed.output).isEqualTo(SUCCESS) assertThat(report.readText()).isEqualTo(active.readText()) assertThat(orphan).exists() + assertThat(file("dd-java-agent/instrumentation/deleted/build/muzzle-deps-results/report.csv")).exists() writeSettings(currentSettings.replace(":instrumentation:demo", ":instrumentation:renamed")) - writeFile("dd-java-agent/instrumentation/renamed/build.gradle.kts", producerScript) - val renamed = writeFile("relocated/build/muzzle-deps-results/dd-java-agent_instrumentation_renamed.csv", + writeFile("dd-java-agent/instrumentation/renamed/build.gradle.kts", reportProducerScript) + writeAggregationProject("renamed") + val renamed = writeFile("dd-java-agent/instrumentation/renamed/versions.csv", """ instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion renamed-instrumentation,com.example,demo,1.5.0,1.5.0 @@ -239,9 +219,10 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { assertThat(report.readText()).isEqualTo(renamed.readText()) assertThat(active).exists() assertThat(orphan).exists() + assertThat(file("dd-java-agent/instrumentation/demo/build/muzzle-deps-results/report.csv")).exists() val reused = run(*args) - assertThat(reused.task(args[0])?.outcome).describedAs(reused.output).isEqualTo(UP_TO_DATE) + assertThat(reused.task(args[0])?.outcome).describedAs(reused.output).isEqualTo(SUCCESS) assertThat(reused.output).contains("Reusing configuration cache") assertThat(report.readText()).isEqualTo(renamed.readText()) } @@ -289,4 +270,48 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { .describedAs(reused.output).isEqualTo(SUCCESS) assertThat(reused.output).contains("Reusing configuration cache") } + + private fun writeAggregationProject(vararg producers: String) { + writeFile("dd-java-agent/instrumentation/build.gradle.kts", + """ + plugins { + id("java") + id("dd-trace-java.muzzle-report-aggregation") + } + layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) + muzzleReports.reportFile.set(layout.buildDirectory.file("muzzle-deps-results/dd-java-agent_instrumentation.csv")) + dependencies { + ${producers.joinToString("\n") { "implementation(project(\":dd-java-agent:instrumentation:$it\"))" }} + } + """ + ) + } + + // Model local report producers without Maven so mutations test the aggregation pipeline alone. + private val reportProducerScript = """ + import org.gradle.api.attributes.Category + import org.gradle.api.attributes.VerificationType + + plugins { id("java") } + val output = layout.buildDirectory.file("muzzle-deps-results/report.csv") + val report = tasks.register("publishReport") { + val source = layout.projectDirectory.file("versions.csv") + val reportOutput = output + inputs.files(files(source).asFileTree) + outputs.file(reportOutput) + doLast { + val target = reportOutput.get().asFile + target.parentFile.mkdirs() + target.writeText(if (source.asFile.exists()) source.asFile.readText() + else "instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion\n") + } + } + configurations.consumable("muzzleReportElements") { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.VERIFICATION)) + attribute(VerificationType.VERIFICATION_TYPE_ATTRIBUTE, objects.named(VerificationType::class.java, "muzzle-dependency-report")) + } + outgoing.artifact(output) { builtBy(report) } + } + """ } diff --git a/dd-java-agent/instrumentation/build.gradle b/dd-java-agent/instrumentation/build.gradle index 592b60261f7..d544b426ce2 100644 --- a/dd-java-agent/instrumentation/build.gradle +++ b/dd-java-agent/instrumentation/build.gradle @@ -4,6 +4,7 @@ plugins { id 'com.gradleup.shadow' id 'dd-trace-java.module.product-subsystem' id("dd-trace-java.instrumentation-naming") + id("dd-trace-java.muzzle-report-aggregation") } tasks.register("latestDepTest", Test) @@ -22,16 +23,14 @@ dependencies { implementation project(':dd-java-agent:agent-installer') } -if (project.gradle.startParameter.taskNames.any { it.endsWith("generateMuzzleReport") }) { - apply plugin: 'dd-trace-java.muzzle' - tasks.register('muzzleInstrumentationReport') { - dependsOn(subprojects.collect { p -> - p.tasks.matching { - it.name == "generateMuzzleReport" - } - }) - finalizedBy(tasks.named('mergeMuzzleReports')) - } +muzzleReports { + reportFile = layout.buildDirectory.file('muzzle-deps-results/dd-java-agent_instrumentation.csv') +} + +tasks.register('muzzleInstrumentationReport') { + group = 'Muzzle' + description = 'Generate the aggregate instrumentation dependency version report' + dependsOn(tasks.named('aggregateMuzzleReports')) } tasks.named('shadowJar', ShadowJar) { diff --git a/docs/how_instrumentations_work.md b/docs/how_instrumentations_work.md index d1c463c8a39..b81673073ec 100644 --- a/docs/how_instrumentations_work.md +++ b/docs/how_instrumentations_work.md @@ -79,8 +79,13 @@ the shared build logic continues to evolve behind the plugin. ## Muzzle -Muzzle directives are applied at build time from the `build.gradle` file. -OpenTelemetry provides some [Muzzle documentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/contributing/muzzle.md). +### Muzzle checks + +`muzzle` validates instrumentation compatibility against library versions. + +The compatibility is checked against _Muzzle directives_ that are applied +at build time from the `build.gradle` file. OpenTelemetry provides some +[Muzzle documentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/contributing/muzzle.md). Muzzle directives check for a range of framework versions that are safe to load the instrumentation. See this excerpt as an example from [rediscala](../dd-java-agent/instrumentation/rediscala-1.8/build.gradle): @@ -123,6 +128,25 @@ By default, all the muzzle directives are checked against all the instrumentatio 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. +### Muzzle Dependency Range Reports + +The _Muzzle directives_ are also used to produce a report summarizing +dependency version ranges; to aggregate all instrumentation reports run +`./gradlew :dd-java-agent:instrumentation:aggregateMuzzleReports`. + +The report will be written to `dd-java-agent/instrumentation/build/muzzle-deps-results/dd-java-agent_instrumentation.csv`. + +**Example content:** + +```csv +instrumentation,jarGroupId,jarArtifactId,lowestVersion,highestVersion +aerospike,com.aerospike,aerospike-client,4.0.0,7.2.3 +akka-http,com.typesafe.akka,akka-http_2.12,10.0.0,10.5.3 +amqp,com.rabbitmq,amqp-client,2.7.0,5.37.0 +apache-httpclient,org.apache.httpcomponents.client5,httpclient5,5.0,5.7-alpha1 +``` + + ### Identifying Breaking Changes with JApiCmp Before defining muzzle version ranges, you can use the JApiCmp plugin to compare different versions of a library and From 70cb396776f37cf2ffa3b5a5b2b904b67a5efd87 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Wed, 7 Oct 2026 17:42:56 +0200 Subject: [PATCH 2/4] chore: Tweak task descriptions --- build.gradle.kts | 2 +- .../gradle/plugin/muzzle/MuzzlePlugin.kt | 1 + .../muzzle/MuzzleReportAggregationPlugin.kt | 2 +- .../muzzle/tasks/MuzzleAggregateReportTask.kt | 2 +- .../muzzle/tasks/MuzzleGenerateReportTask.kt | 2 +- .../gradle/plugin/muzzle/tasks/MuzzleTask.kt | 2 +- .../muzzle/MuzzleReportAggregationTest.kt | 21 +++++++++++++++++++ dd-java-agent/instrumentation/build.gradle | 2 +- 8 files changed, 28 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ea58625770e..01420f2c92c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -130,7 +130,7 @@ tasks.register("latestDepTest") tasks.register("generateMuzzleReport") { group = "Muzzle" - description = "Generate the aggregate instrumentation dependency version report" + description = "Aggregate instrumentation dependency range reports" dependsOn(":dd-java-agent:instrumentation:aggregateMuzzleReports") } diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt index 269069f354d..42379795a86 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt @@ -75,6 +75,7 @@ class MuzzlePlugin : Plugin { } val muzzleTask = project.tasks.register("muzzle") { + description = "Check instrumentation compatibility against the configured dependency versions" this.muzzleBootstrap.set(muzzleBootstrap) this.muzzleTooling.set(muzzleTooling) dependsOn(compileMuzzle) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt index d71cc2d76af..73bfcbac6e3 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationPlugin.kt @@ -47,7 +47,7 @@ class MuzzleReportAggregationPlugin : Plugin { project.tasks.register("mergeMuzzleReports") { group = "Muzzle" - description = "Deprecated: use aggregateMuzzleReports for the aggregate dependency report" + description = "Deprecated: use aggregateMuzzleReports for dependency range reports" dependsOn(report) doFirst { logger.warn("mergeMuzzleReports is deprecated; use aggregateMuzzleReports") } } diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleAggregateReportTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleAggregateReportTask.kt index 95b8558a49d..0a3dc9e4a35 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleAggregateReportTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleAggregateReportTask.kt @@ -12,7 +12,7 @@ import java.util.TreeMap abstract class MuzzleAggregateReportTask : AbstractMuzzleReportTask() { init { - description = "Aggregate instrumentation dependency version reports" + description = "Aggregate instrumentation dependency range reports" versionsFile.convention(project.layout.buildDirectory.file("$MUZZLE_DEPS_RESULTS/muzzle.csv")) // Preserve the existing order-sensitive handling of Maven-equivalent version spellings. outputs.upToDateWhen { false } diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt index 361f3449bca..272c88cb835 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleGenerateReportTask.kt @@ -34,7 +34,7 @@ abstract class MuzzleGenerateReportTask : AbstractMuzzleReportTask() { } init { - description = "Generate this instrumentation's dependency version report" + description = "Generate this instrumentation's dependency range report" reportDirectives.convention(emptyList()) versionsFile.convention(project.layout.buildDirectory.file("$MUZZLE_DEPS_RESULTS/${project.pathSlug}.csv")) // Repository metadata can change without any local task input changing. diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt index 45b3f84272b..926b8abed43 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt @@ -35,7 +35,7 @@ abstract class MuzzleTask @Inject constructor( objects: ObjectFactory, providers: ProviderFactory, ) : AbstractMuzzleTask() { - override fun getDescription(): String = if (muzzleDirective.isPresent) { + override fun getDescription(): String = super.getDescription() ?: if (muzzleDirective.isPresent) { "Run instrumentation muzzle on ${muzzleDirective.get().name} dependency" } else { "Run instrumentation muzzle on compile time dependencies" diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt index 04dd239a039..99b7c2216ac 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt @@ -7,6 +7,27 @@ import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.junit.jupiter.api.Test class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { + @Test + fun `help distinguishes compatibility validation from dependency reports`() { + writeReportingProject("muzzle { pass { coreJdk() } }") + val descriptions = mapOf( + "demo:muzzle" to "Check instrumentation compatibility against the configured dependency versions", + "demo:generateMuzzleReport" to "Generate this instrumentation's dependency range report", + "aggregateMuzzleReports" to "Aggregate instrumentation dependency range reports", + "mergeMuzzleReports" to "Deprecated: use aggregateMuzzleReports for dependency range reports" + ) + descriptions.forEach { (task, description) -> + val result = run( + "help", "--task", ":dd-java-agent:instrumentation:$task", + "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace" + ) + assertThat(result.task(":help")?.outcome).describedAs(result.output).isEqualTo(SUCCESS) + assertThat(result.output).contains(description) + assertThat(result.tasks).noneMatch { it.path.contains("muzzle", ignoreCase = true) } + } + assertThat(file(REPORT)).doesNotExist() + } + @Test fun `aggregates only published reports and refreshes Maven ranges with configuration cache reuse`() { val repo = createMavenRepoFixture() diff --git a/dd-java-agent/instrumentation/build.gradle b/dd-java-agent/instrumentation/build.gradle index d544b426ce2..fbacf010b3c 100644 --- a/dd-java-agent/instrumentation/build.gradle +++ b/dd-java-agent/instrumentation/build.gradle @@ -29,7 +29,7 @@ muzzleReports { tasks.register('muzzleInstrumentationReport') { group = 'Muzzle' - description = 'Generate the aggregate instrumentation dependency version report' + description = 'Aggregate instrumentation dependency range reports' dependsOn(tasks.named('aggregateMuzzleReports')) } From 77a73717188378135a1acba823a3e46bc5839c04 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Wed, 7 Oct 2026 18:10:32 +0200 Subject: [PATCH 3/4] refactor: invoke Muzzle aggregation directly --- .gitlab-ci.yml | 2 +- build.gradle.kts | 6 ---- .../muzzle/MuzzleReportAggregationTest.kt | 36 ++++++++++++------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a4d7e359f73..815c0771268 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -742,7 +742,7 @@ muzzle-dep-report: script: - export SKIP_BUILDSCAN="true" - ./gradlew --version - - ./gradlew generateMuzzleReport muzzleInstrumentationReport $GRADLE_ARGS + - ./gradlew :dd-java-agent:instrumentation:aggregateMuzzleReports $GRADLE_ARGS after_script: - *container_info - *cgroup_info diff --git a/build.gradle.kts b/build.gradle.kts index 01420f2c92c..30606877d37 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -128,12 +128,6 @@ allprojects { tasks.register("latestDepTest") -tasks.register("generateMuzzleReport") { - group = "Muzzle" - description = "Aggregate instrumentation dependency range reports" - dependsOn(":dd-java-agent:instrumentation:aggregateMuzzleReports") -} - nexusPublishing { repositories { val forceLocal = providers.gradleProperty("forceLocal").getOrElse("false").toBoolean() diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt index 99b7c2216ac..5eadea3f581 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt @@ -81,7 +81,10 @@ class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { writeFile("relocated/build/muzzle-deps-results/stale.csv", HEADER + "stale,example,library,0,99") writeFile(REPORT, HEADER + "previous,example,library,0,99") - val args = arrayOf(":generateMuzzleReport", "--configuration-cache", "--configuration-cache-problems=fail", "--build-cache", "--stacktrace") + val args = arrayOf( + ":dd-java-agent:instrumentation:aggregateMuzzleReports", + "--configuration-cache", "--configuration-cache-problems=fail", "--build-cache", "--stacktrace" + ) val env = mapOf("MAVEN_REPOSITORY_PROXY" to repo.repoUrl) val first = run(*args, env = env) assertThat(first.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(first.output).isEqualTo(SUCCESS) @@ -104,7 +107,7 @@ class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { assertThat(second.task(":dd-java-agent:instrumentation:demo:generateMuzzleReport")?.outcome).isEqualTo(SUCCESS) assertThat(second.task(":dd-java-agent:instrumentation:nested:other:generateMuzzleReport")?.outcome).isEqualTo(SUCCESS) assertThat(second.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).isEqualTo(SUCCESS) - assertThat(second.task(":generateMuzzleReport")?.outcome).isIn(SUCCESS, UP_TO_DATE) + assertThat(second.task(":generateMuzzleReport")).isNull() repo.publishVersions("example", "library", listOf("4.0")) val refreshed = run(*args, env = env) @@ -135,7 +138,11 @@ class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { assertThat(help.tasks).noneMatch { it.path.contains("Muzzle") || it.path.contains("muzzle") } assertThat(file(REPORT)).doesNotExist() - val failed = run(":generateMuzzleReport", "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace", expectFailure = true, env = env) + val failed = run( + ":dd-java-agent:instrumentation:aggregateMuzzleReports", + "--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace", + expectFailure = true, env = env + ) assertThat(failed.output).contains("BUILD FAILED") assertThat(failed.task(":dd-java-agent:instrumentation:demo:generateMuzzleReport")?.outcome).isEqualTo(FAILED) assertThat(failed.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")).isNull() @@ -292,13 +299,18 @@ class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { } @Test - fun `aggregate aliases delegate to instrumentation with the relocated CI artifact path`() { + fun `aggregation and aliases preserve the relocated CI artifact path without a root report task`() { writeReportingProject("muzzle { pass { coreJdk() } }") val cc = arrayOf("--configuration-cache", "--configuration-cache-problems=fail", "--stacktrace") - val ci = run("generateMuzzleReport", "muzzleInstrumentationReport", *cc) - assertThat(ci.task(":generateMuzzleReport")?.outcome).describedAs(ci.output).isIn(SUCCESS, UP_TO_DATE) - assertThat(ci.task(":dd-java-agent:instrumentation:muzzleInstrumentationReport")?.outcome).isIn(SUCCESS, UP_TO_DATE) + val ci = run(":dd-java-agent:instrumentation:aggregateMuzzleReports", *cc) + assertThat(ci.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(ci.output).isEqualTo(SUCCESS) + assertThat(ci.task(":generateMuzzleReport")).isNull() + assertThat(ci.task(":dd-java-agent:instrumentation:muzzleInstrumentationReport")).isNull() assertThat(file(REPORT).readText()).isEqualTo(HEADER) + + val missingRootTask = run(":generateMuzzleReport", expectFailure = true) + assertThat(missingRootTask.output).contains("'generateMuzzleReport' not found in root project") + listOf("mergeMuzzleReports", "muzzleInstrumentationReport").forEach { alias -> val taskPath = ":dd-java-agent:instrumentation:$alias" val first = run(taskPath, *cc) @@ -333,14 +345,17 @@ class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { } """ ) - val args = arrayOf(":generateMuzzleReport", "--isolated-projects", "--configuration-cache-problems=fail", "--stacktrace") + val args = arrayOf( + ":dd-java-agent:instrumentation:aggregateMuzzleReports", + "--isolated-projects", "--configuration-cache-problems=fail", "--stacktrace" + ) val first = run(*args) assertThat(first.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(first.output).isEqualTo(SUCCESS) assertThat(first.output).contains("Configuration cache entry stored") assertThat(file(REPORT).readText()).isEqualTo(HEADER) val reused = run(*args) assertThat(reused.task(":dd-java-agent:instrumentation:aggregateMuzzleReports")?.outcome).describedAs(reused.output).isEqualTo(SUCCESS) - assertThat(reused.task(":generateMuzzleReport")?.outcome).describedAs(reused.output).isIn(SUCCESS, UP_TO_DATE) + assertThat(reused.task(":generateMuzzleReport")).isNull() assertThat(reused.output).contains("Reusing configuration cache") } @@ -394,9 +409,6 @@ class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { """ plugins { id("dd-trace-java.muzzle-report-aggregation") apply false } layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) - tasks.register("generateMuzzleReport") { - dependsOn(":dd-java-agent:instrumentation:aggregateMuzzleReports") - } """ ) } From a2aba82c17fdb7557f0061ca0eca8933a57b0408 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Thu, 8 Oct 2026 10:08:22 +0200 Subject: [PATCH 4/4] refactor: share Muzzle report test setup --- .../plugin/muzzle/MuzzlePluginTestFixture.kt | 24 +++++++++++++++++++ .../muzzle/MuzzleReportAggregationTest.kt | 22 ----------------- .../MuzzleReportConfigurationCacheTest.kt | 16 ------------- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginTestFixture.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginTestFixture.kt index 2ba95298c73..5673e0cba1b 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginTestFixture.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginTestFixture.kt @@ -41,6 +41,30 @@ open class MuzzlePluginTestFixture : GradleFixture() { addSubproject("dd-java-agent:instrumentation:demo", instrumentationBuildScript) } + /** Writes an aggregation project with optional instrumentation report producers. */ + fun writeAggregationProject(vararg producers: String) { + addSubproject("dd-java-agent:instrumentation", + """ + plugins { + id("java") + id("dd-trace-java.muzzle-report-aggregation") + } + layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) + muzzleReports.reportFile.set(layout.buildDirectory.file("muzzle-deps-results/dd-java-agent_instrumentation.csv")) + dependencies { + ${producers.joinToString("\n") { "implementation(project(\":dd-java-agent:instrumentation:$it\"))" }} + } + tasks.register("muzzleInstrumentationReport") { dependsOn(tasks.named("aggregateMuzzleReports")) } + """ + ) + writeRootProject( + """ + plugins { id("dd-trace-java.muzzle-report-aggregation") apply false } + layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) + """ + ) + } + /** * Writes a muzzle scan plugin that always passes. */ diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt index 5eadea3f581..dedd300e0cb 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportAggregationTest.kt @@ -391,28 +391,6 @@ class MuzzleReportAggregationTest : MuzzlePluginTestFixture() { ) } - private fun writeAggregationProject() { - addSubproject("dd-java-agent:instrumentation", - """ - plugins { - id("java") - id("dd-trace-java.muzzle-report-aggregation") - } - layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) - muzzleReports { - reportFile.set(layout.buildDirectory.file("muzzle-deps-results/dd-java-agent_instrumentation.csv")) - } - tasks.register("muzzleInstrumentationReport") { dependsOn(tasks.named("aggregateMuzzleReports")) } - """ - ) - writeRootProject( - """ - plugins { id("dd-trace-java.muzzle-report-aggregation") apply false } - layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) - """ - ) - } - private fun addAggregationInput(projectPath: String, script: String) { addSubproject(projectPath, script) writeFile("dd-java-agent/instrumentation/build.gradle.kts", diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt index cc3184f165e..f7f1724ed29 100644 --- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt +++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzleReportConfigurationCacheTest.kt @@ -271,22 +271,6 @@ class MuzzleReportConfigurationCacheTest : MuzzlePluginTestFixture() { assertThat(reused.output).contains("Reusing configuration cache") } - private fun writeAggregationProject(vararg producers: String) { - writeFile("dd-java-agent/instrumentation/build.gradle.kts", - """ - plugins { - id("java") - id("dd-trace-java.muzzle-report-aggregation") - } - layout.buildDirectory.set(layout.projectDirectory.dir("relocated/build")) - muzzleReports.reportFile.set(layout.buildDirectory.file("muzzle-deps-results/dd-java-agent_instrumentation.csv")) - dependencies { - ${producers.joinToString("\n") { "implementation(project(\":dd-java-agent:instrumentation:$it\"))" }} - } - """ - ) - } - // Model local report producers without Maven so mutations test the aggregation pipeline alone. private val reportProducerScript = """ import org.gradle.api.attributes.Category