Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,8 @@ class MuzzlePlugin : Plugin<Project> {

val validationTasks = MuzzleValidationTasks(project.providers) {
val startTime = System.currentTimeMillis()
val system = MuzzleMavenRepoUtils.newRepositorySystem()
val session = MuzzleMavenRepoUtils.newRepositorySystemSession(system)
val taskPlanner = MuzzleTaskPlanner.from(system, session)
val directives = project.extensions.getByType<MuzzleExtension>().directives
val checks = taskPlanner.plan(directives).map { plan ->
val checks = sharedTaskPlanner(project).plan(directives).map { plan ->
val check = registerMuzzleTask(plan.directive, plan.artifact, project, muzzleBootstrap, muzzleTooling)
project.logger.info("configured ${plan.directive}")
check
Expand Down Expand Up @@ -142,6 +139,24 @@ class MuzzlePlugin : Plugin<Project> {
}
}

private fun sharedTaskPlanner(project: Project): MuzzleTaskPlanner {
val properties = project.rootProject.extensions.extraProperties

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: I'd avoid the extra properties here. It's not typed, and the access is not coordinated, it happens via hasProperty / setProperty which looks like configuring another project.

Maybe a service could better fit? I'm unsure at this stage.

val key = "datadogMuzzleTaskPlanners"
if (!properties.has(key)) {
properties.set(key, mutableMapOf<List<List<Triple<String, String, String>>>, MuzzleTaskPlanner>())
}
@Suppress("UNCHECKED_CAST")
val planners = properties.get(key) as MutableMap<List<List<Triple<String, String, String>>>, MuzzleTaskPlanner>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: This is a long type. Maybe using a kotlin type alias or a concrete value class for the key could help. Also that's the issue with extra properties it's not typed.

Also, I think using the map directly here could burn us.

I tend to think that making the whole thing a service offering some method. E.g. a registrar that return a planer. Or even higher level method returning a MuzzleValidationTasks.

// Repository IDs can be reused for different URLs, so keep their local metadata separate.
val repositories = project.extensions.getByType<MuzzleExtension>().directives
.map { it.additionalRepositories.toList() }.distinct()
return planners.getOrPut(repositories) {
val system = MuzzleMavenRepoUtils.newRepositorySystem()
val session = MuzzleMavenRepoUtils.newRepositorySystemSession(system)
MuzzleTaskPlanner.from(system, session)
}
}

companion object {
/**
* Registers a new muzzle task for the given directive and artifact.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package datadog.gradle.plugin.muzzle

import datadog.gradle.plugin.MavenRepoFixture
import org.assertj.core.api.Assertions.assertThat
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -31,6 +32,61 @@ class MuzzlePluginFunctionalTest : MuzzlePluginTestFixture() {
assertThat(result.task(":dd-java-agent:instrumentation:demo:muzzle-AssertPass-core-jdk")?.outcome).isEqualTo(SUCCESS)
}

@Test
fun `planning sees directives configured after project evaluation`() {
writeProject(
"""
plugins {
id("java")
id("dd-trace-java.muzzle")
}
"""
)
writeNoopScanPlugin()
writeRootProject(
"""
plugins { id("dd-trace-java.muzzle") apply false }
gradle.projectsEvaluated {
project(":dd-java-agent:instrumentation:demo").extensions.configure<datadog.gradle.plugin.muzzle.MuzzleExtension> {
pass { coreJdk() }
}
}
"""
)
val result = run(":dd-java-agent:instrumentation:demo:muzzle")
assertThat(result.output).contains("BUILD SUCCESSFUL")
assertThat(result.task(":dd-java-agent:instrumentation:demo:muzzle-AssertPass-core-jdk")?.outcome).isEqualTo(SUCCESS)
}

@Test
fun `shared planning keeps module repositories separate`() {
val empty = createMavenRepoFixture()
val first = MavenRepoFixture(projectDir.resolve("first-repository"))
val second = MavenRepoFixture(projectDir.resolve("second-repository"))
first.publishVersions("com.example.test", "shared-lib", listOf("1.0.0"))
second.publishVersions("com.example.test", "shared-lib", listOf("2.0.0"))
fun script(url: String) = """
plugins { id("java"); id("dd-trace-java.muzzle") }
repositories { maven { url = uri("$url") } }
muzzle {
extraRepository("fixture", "$url")
pass { group = "com.example.test"; module = "shared-lib"; versions = "[1.0,3.0)" }
}
"""
writeProject(script(first.repoUrl))
addSubproject("dd-java-agent:instrumentation:other", script(second.repoUrl))
writeNoopScanPlugin()

val result = run("muzzle", env = mapOf("MAVEN_REPOSITORY_PROXY" to empty.repoUrl))

assertThat(result.output).contains("BUILD SUCCESSFUL")
for ((module, version) in listOf("demo" to "1.0.0", "other" to "2.0.0")) {
val prefix = ":dd-java-agent:instrumentation:$module:muzzle-AssertPass-com.example.test-shared-lib-"
assertThat(result.task("$prefix$version")?.outcome).isEqualTo(SUCCESS)
assertThat(result.tasks.filter { it.path.startsWith(prefix) }).hasSize(1)
}
}

@ParameterizedTest
@ValueSource(strings = ["muzzle", ":dd-java-agent:instrumentation:demo:muzzle", "runMuzzle", "verifyCompatibility"])
fun `runs validation through different task entry points`(taskName: String) {
Expand Down
Loading