Skip to content

Commit

Permalink
Micronaut BOM fixes (micronaut-projects#6405)
Browse files Browse the repository at this point in the history
This commit fixes a number of issues with our BOM:

- make sure that all dependencies in the BOM are referenced via
a properties version (e.g `<version>${micronaut.aop}</version>`)
- fixed validation not failing if a POM file wasn't found in any
repository
- fixed validation of BOM dependencies which do not belong to the
BOM group
- made validation systematic
- added configuration options to fail on snapshots, by default it
will fail if a version is *not* SNAPSHOT: releases would fail if
a dependency is a snapshot
- added validation of our own BOM, which was missing
- fixed the netty dependency in our BOM

Fixes micronaut-projects#6404
  • Loading branch information
melix authored Oct 24, 2021
1 parent a6b9ed9 commit dfe036b
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 22 deletions.
9 changes: 0 additions & 9 deletions bom-check/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,3 @@ plugins {
repositories {
mavenCentral()
}

boolean micronautSnapshot = rootProject.version.toString().endsWith("-SNAPSHOT")

tasks.withType(io.micronaut.build.internal.pom.PomChecker).configureEach {
onlyIf {
// We only perform validation on releases
!micronautSnapshot || providers.gradleProperty("force.check.bom").isPresent()
}
}
60 changes: 57 additions & 3 deletions bom/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import io.micronaut.build.internal.pom.VersionCatalogConverter
import org.gradle.api.plugins.catalog.CatalogPluginExtension
import org.gradle.api.plugins.catalog.VersionCatalogPlugin
import io.micronaut.build.internal.pom.PomChecker

plugins {
id 'java-platform'
Expand Down Expand Up @@ -46,7 +47,7 @@ def legacyVersionNames = [
]

String toPropertyName(String alias) {
alias.split("(?=[A-Z])").collect { it.toLowerCase(Locale.US) }.join("-").replace((char)'-', (char)'.')
alias.split("(?=[A-Z])").collect { it.toLowerCase(Locale.US) }.join("-").replace((char) '-', (char) '.')
}

components.javaPlatform.addVariantsFromConfiguration(
Expand All @@ -66,7 +67,7 @@ tasks.named("generateCatalogAsToml") {
}

def bomPropertyName = { String alias ->
alias = alias - 'managed.'
alias = alias - 'managed.' - 'boms.'
String baseName = legacyVersionNames[alias] ?: toPropertyName(alias)
"${baseName}.version"
}
Expand All @@ -85,7 +86,8 @@ publishing {
} + project.ext.pomInfo
modelConverter.model.librariesTable.each { library ->
def alias = library.version.reference.replaceAll('-', '.')
if (alias != null && library.alias.replaceAll('-', '.').startsWith("managed.")) {
def libraryAlias = library.alias?.replaceAll('-', '.')
if (alias != null && libraryAlias.startsWith("managed.") || libraryAlias.startsWith('boms.')) {
def pomDep = xml.dependencyManagement.dependencies.dependency.find {
it.artifactId.text() == library.name
&& it.groupId.text() == library.group
Expand All @@ -97,6 +99,28 @@ publishing {
}
}
}
// Add individual module versions as properties
for (Project p : rootProject.subprojects) {
if (!p.subprojects.empty) {
continue
}
if (p.name.contains("bom")) {
continue
}
if (excludedProjects.contains(p.name)) {
continue
}
String propertyName = "micronaut.${p.name.replace((char) '-', (char) '.')}"
def pomDep = xml.dependencyManagement.dependencies.dependency.find {
it.artifactId.text() == "micronaut-${p.name}"
&& it.groupId.text() == group
}
if (pomDep != null) {
pomDep.version.first().setValue("\${${propertyName}}")
} else {
println "[WARNING] Didn't find dependency ${group}:micronaut-${p.name} in BOM file"
}
}
}
}
libsCatalog.versionAliases.each { alias ->
Expand All @@ -107,6 +131,21 @@ publishing {
}
}
}
// Add individual module versions as properties
for (Project p : rootProject.subprojects) {
if (!p.subprojects.empty) {
continue
}
if (p.name.contains("bom")) {
continue
}
if (excludedProjects.contains(p.name)) {
continue
}
String propertyName = "micronaut.${p.name.replace((char) '-', (char) '.')}"
pom.properties.put(propertyName, p.version)
}

}
}
}
Expand Down Expand Up @@ -157,3 +196,18 @@ dependencies {

}
}

def checkBom = tasks.register("checkBom", PomChecker) {
String repoUrl = publishing.repositories.findByName("Build").url.toString()
repositories.add(repoUrl)
pomFile.fileProvider(tasks.named('generatePomFileForMavenPublication', GenerateMavenPom).map { it.destination })
pomCoordinates.set("${project.group}:micronaut-${project.name}:$version")
checkBomContents = false
report = layout.buildDirectory.file("reports/boms/micronaut-bom.txt")
failOnSnapshots = !project.version.endsWith('-SNAPSHOT')
failOnError = true
}

tasks.named("check") {
dependsOn(checkBom)
}
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ subprojects { Project subproject ->
shadowJarEnabled = false
}

group projectGroupId

if (subproject.name.contains("bom") || subproject.name.contains("parent")) {
return
}

group projectGroupId

apply plugin: "io.micronaut.build.internal.common"
if (!subproject.name.startsWith('test-') &&
!subproject.name.startsWith('inject-test-') &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction

import static org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP
Expand All @@ -25,6 +29,20 @@ abstract class PomChecker extends DefaultTask {
@Input
abstract Property<String> getPomCoordinates()

@Input
abstract Property<Boolean> getFailOnSnapshots()

@Input
abstract Property<Boolean> getFailOnError()

@Input
abstract Property<Boolean> getFailOnBomContents()

@InputFile
@PathSensitive(PathSensitivity.NONE)
@Optional
abstract RegularFileProperty getPomFile()

@Input
abstract Property<Boolean> getCheckBomContents()

Expand All @@ -34,31 +52,52 @@ abstract class PomChecker extends DefaultTask {
PomChecker() {
description = "Verifies a POM file"
group = VERIFICATION_GROUP
getFailOnError().convention(true)
getFailOnSnapshots().convention(getPomCoordinates().map(v -> !v.endsWith("-SNAPSHOT")))
getFailOnBomContents().convention(false)
}

@TaskAction
void verifyBom() {
List<String> errors = []
boolean found = false
for (String repositoryUrl : repositories.get()) {
def coordinates = pomCoordinates.get().split(':')
if (coordinates.length != 3) {
throw new GradleException("Incorrect BOM coordinates '${pomCoordinates.get()}': should be of the form group:artifact:version ")
}
def (group, artifact, version) = [coordinates[0], coordinates[1], coordinates[2]]
if (repositoryUrl.endsWith('/')) {
repositoryUrl = repositoryUrl.substring(0, repositoryUrl.length() - 1)
}
def uri = "$repositoryUrl/${group.replace((char) '.', (char) '/')}/${artifact}/${version}/${artifact}-${version}.pom"
try {
def pom = new XmlSlurper().parse(uri)
def pom
if (pomFile.present) {
pom = new XmlSlurper().parse(pomFile.asFile.get())
} else {
pom = new XmlSlurper().parse(uri)
}
if (checkBomContents.get()) {
assertDependencyManagementConfinedToGroup(pom, group, artifact, errors)
assertDependencyManagementConfinedToGroup(pom, group, artifact, errors)
}
if (version.endsWith("-SNAPSHOT")) {
errors << "Dependency ${pomCoordinates.get()} is a SNAPSHOT".toString()
String message = "Dependency ${pomCoordinates.get()} is a SNAPSHOT"
if (failOnSnapshots.get()) {
errors << message
} else {
logger.warn(message)
}
}
return
found = true
break
} catch (Exception ex) {
getLogger().debug("Skipping repository $repositoryUrl as the POM file is missing or corrupt")
}
}
if (!found) {
errors << "Dependency ${pomCoordinates.get()} is wasn't found in any repository".toString()
}
def reportFile = report.asFile.get()
def reportDir = reportFile.parentFile
if (reportDir.directory || reportDir.mkdirs()) {
Expand All @@ -67,16 +106,29 @@ abstract class PomChecker extends DefaultTask {
throw new GradleException("Unable to write report file to ${reportFile}")
}
if (errors) {
throw new GradleException("Validation failed for ${pomCoordinates.get()}. Check the report at ${reportFile} for details.")
String message = "Validation failed for ${pomCoordinates.get()}. Check the report at ${reportFile} for details."
if (failOnError.get()) {
throw new GradleException(message)
} else {
logger.error(message)
}
}
}

@CompileDynamic
private static void assertDependencyManagementConfinedToGroup(GPathResult pom, String group, String name, List<String> errors) {
private void assertDependencyManagementConfinedToGroup(GPathResult pom, String group, String name, List<String> errors) {
pom.dependencyManagement.dependencies.dependency.each {
String depGroup = it.groupId.text().replace('${project.groupId}', group)
if (!depGroup.startsWith(group)) {
errors << "Error validating BOM [${name}]: includes the dependency [${it.groupId}:${it.artifactId}:${it.version}] which doesn't start with the group id of the BOM: [${group}]".toString()
def scope = it.scope.text()
if (scope != 'import') {
String message = "Error validating BOM [${name}]: includes the dependency [${it.groupId}:${it.artifactId}:${it.version}] which doesn't start with the group id of the BOM: [${group}]"
if (failOnBomContents.get()) {
errors << message
} else {
logger.warn(message)
}
}
}
}
}
Expand Down
1 change: 0 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ managed-mongo-reactive = { module = "org.mongodb:mongodb-driver-reactivestreams"
managed-neo4j = { module = "org.neo4j.test:neo4j-harness", version.ref = "managed-neo4j" }
managed-neo4j-bolt = { module = "org.neo4j.driver:neo4j-java-driver", version.ref = "managed-neo4j-java-driver" }

managed-netty = { module = "io.netty:netty", version.ref = "managed-netty" }
managed-netty-buffer = { module = "io.netty:netty-buffer", version.ref = "managed-netty" }
managed-netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "managed-netty" }
managed-netty-codec-http2 = { module = "io.netty:netty-codec-http2", version.ref = "managed-netty" }
Expand Down

0 comments on commit dfe036b

Please sign in to comment.