Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BOM generation. #1569

Merged
merged 8 commits into from
Feb 20, 2025
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
2 changes: 1 addition & 1 deletion .evergreen/publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ fi
SYSTEM_PROPERTIES="-Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.internal.http.connectionTimeout=120000 -Dorg.gradle.internal.http.socketTimeout=120000"

./gradlew -version
./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info ${TASK}
./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info ${TASK} # Scala 2.13 is published as result of this gradle execution.
./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info :bson-scala:${TASK} :driver-scala:${TASK} -PdefaultScalaVersions=2.12.12
./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info :bson-scala:${TASK} :driver-scala:${TASK} -PdefaultScalaVersions=2.11.12
23 changes: 23 additions & 0 deletions bom/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
group = "org.mongodb"
description = "This Bill of Materials POM simplifies dependency management when referencing multiple" +
" MongoDB Java Driver artifacts in projects using Gradle or Maven."

dependencies {
constraints {
api(project(":mongodb-crypt"))
api(project(":driver-core"))
api(project(":bson"))
api(project(":bson-record-codec"))

api(project(":driver-sync"))
api(project(":driver-reactive-streams"))

api(project(":bson-kotlin"))
api(project(":bson-kotlinx"))
api(project(":driver-kotlin-coroutine"))
api(project(":driver-kotlin-sync"))

api(project(":bson-scala"))
api(project(":driver-scala"))
}
}
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ ext {

def configDir = ext.configDir
def utilProjects = project(":util").allprojects
def bomProjects = project(":bom")
def coreProjects = subprojects - utilProjects
def scalaProjects = subprojects.findAll { it.name.contains('scala') }
def javaProjects = subprojects - scalaProjects
def scalaProjects = subprojects.findAll { it.name.contains('scala') } - bomProjects
def javaProjects = subprojects - scalaProjects - bomProjects
def javaMainProjects = javaProjects - utilProjects
def javaCodeCheckedProjects = javaMainProjects.findAll { !['driver-benchmarks', 'driver-workload-executor', 'driver-lambda'].contains(it.name) }
def javaAndScalaTestedProjects = javaCodeCheckedProjects + scalaProjects
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

org.gradle.daemon=true
org.gradle.jvmargs=-Duser.country=US -Duser.language=en
## NOTE: This property is also used to generate scala compile versions in BOM.
scalaVersions=2.11.12,2.12.20,2.13.15
defaultScalaVersions=2.13.15
runOnceTasks=clean,release
Expand Down
102 changes: 99 additions & 3 deletions gradle/publish.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ ext {
def projectNamesNotToBePublished = ["driver-benchmarks", "driver-lambda", "driver-workload-executor", "graalvm-native-image-app", "util",
"spock", "taglets"]
def publishedProjects = subprojects.findAll { !projectNamesNotToBePublished.contains(it.name) }
def scalaProjects = publishedProjects.findAll { it.name.contains('scala') }
def javaProjects = publishedProjects - scalaProjects
def projectsWithManifest = publishedProjects.findAll {it.name != 'driver-legacy' }
def bomProjects = project(":bom")
def scalaProjects = publishedProjects.findAll { it.name.contains('scala') } - bomProjects
def javaProjects = publishedProjects - scalaProjects - bomProjects
def projectsWithManifest = publishedProjects.findAll {it.name != 'driver-legacy' } - bomProjects

configure(javaProjects) { project ->
apply plugin: 'maven-publish'
Expand Down Expand Up @@ -169,3 +170,98 @@ configure(projectsWithManifest) { project ->
jar configureJarManifestAttributes(project)
}
}

configure(bomProjects) { project ->
apply plugin: 'maven-publish'
apply plugin: 'signing'
apply plugin: 'java-platform'

// Get the Scala versions from the project property. Only major.minor versions.
def scalaVersions = project.findProperty("scalaVersions")?.split(",")
?.collect { it.split("\\.")[0] + "." + it.split("\\.")[1] }

assert scalaVersions != null && !scalaVersions.isEmpty() : "Scala versions must be provided as a comma-separated list" +
" in the 'scalaVersions' project property"

publishing {
publications {
mavenJava(MavenPublication) {
artifactId = "bom".equals(project.archivesBaseName) ? "mongodb-driver-bom" : project.archivesBaseName
from components.javaPlatform

// Modify the generated POM to add multiple compile versions of driver-scala or bson-scala.
Copy link
Member

Choose a reason for hiding this comment

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

Great catch 👍

// Scala multi-version support generates only one for BOM.
pom.withXml {
def pomXml = asNode()

def dependencyManagementNode = pomXml.get("dependencyManagement")?.getAt(0)
assert dependencyManagementNode : "<dependencyManagement> node not found in the generated BOM POM"

def dependenciesNode = dependencyManagementNode.get("dependencies")?.getAt(0)
assert dependenciesNode : "<dependencies> node not found inside <dependencyManagement>"

// Check if scala dependencies are present in the BOM.
def existingScalaDeps = dependenciesNode.children().findAll {
it.artifactId.text().contains("scala")
}

existingScalaDeps.each { existingDep ->
String groupId = existingDep.groupId.text()
String originalArtifactId = existingDep.artifactId.text()
String artifactVersion = existingDep.version.text()

// Add multiple versions with Scala suffixes for each Scala-related dependency.
scalaVersions.each { scalaVersion ->
// Remove existing Scala version suffix (_2.12, _2.13, etc.)
String baseArtifactId = originalArtifactId.replaceAll("_\\d+\\.\\d+(\\.\\d+)?\$", "")
String newArtifactId = "${baseArtifactId}_${scalaVersion}"

// Skip if Scala dependency with this scalaVersion already exists in BOM.
if(newArtifactId != originalArtifactId) {
def dependencyNode = dependenciesNode.appendNode("dependency")
dependencyNode.appendNode("groupId", groupId)
dependencyNode.appendNode("artifactId", newArtifactId)
dependencyNode.appendNode("version", artifactVersion)
}
}
}
}
}
}

repositories configureMavenRepositories(project)
}

afterEvaluate {
publishing.publications.mavenJava.pom configurePom(project)
signing {
useInMemoryPgpKeys(findProperty("signingKey"), findProperty("signingPassword"))
sign publishing.publications.mavenJava
}
}

tasks.withType(GenerateModuleMetadata) {
enabled = false
}

tasks.withType(GenerateMavenPom).configureEach {
doLast {
def xml = file(destination).text
def root = new groovy.xml.XmlSlurper().parseText(xml)

def dependencies = root.dependencyManagement.dependencies.children()
assert dependencies.children().size() > 1 : "BOM must contain more then one <dependency> element:\n$destination"

dependencies.each { dependency ->
def groupId = dependency.groupId.text()
assert groupId.startsWith('org.mongodb') : "BOM must contain only 'org.mongodb' dependencies, but found '$groupId':\n$destination"
/* The <scope> and <optional> tags should be omitted in BOM dependencies.
This ensures that consuming projects have the flexibility to decide whether a
dependency is optional in their context. The BOM's role is to provide version information,
not to dictate inclusion or exclusion of dependencies. */
assert dependency.scope.size() == 0 : "BOM must not contain <scope> elements in dependency:\n$destination"
assert dependency.optional.size() == 0 : "BOM must not contain <optional> elements in dependency:\n$destination"
}
}
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ include ':driver-scala'
include ':mongodb-crypt'
include 'util:spock'
include 'util:taglets'
include ':bom'

if(hasProperty("includeGraalvm")) {
include ':graalvm-native-image-app'
Expand Down