Das Maven-Publish-Plugin transformiert Gradle-Projekte in publizierbare Maven-Artefakte. In Multi-Projekt-Umgebungen entstehen dabei mehrere zusammenhängende Artefakte, die koordiniert versioniert und publiziert werden müssen. Jedes Subprojekt kann eigene Artefakte generieren, die als separate Maven-Module in Repositories landen. Diese Artefakte folgen Maven-Konventionen mit Group-ID, Artifact-ID und Version, wodurch sie von anderen Build-Tools konsumierbar werden.
Die Publication-Konfiguration definiert, welche Artefakte publiziert werden und welche Metadaten sie enthalten. Ein typisches Java-Library-Projekt publiziert JAR-Dateien mit kompiliertem Code, Source-JARs für IDE-Integration und JavaDoc-JARs für Dokumentation. Das generierte POM-File beschreibt das Modul und seine Abhängigkeiten im Maven-Format. Diese Metadaten ermöglichen transitives Dependency-Management für Konsumenten der Artefakte.
Multi-Projekt-Builds erfordern besondere Überlegungen bei der Publication-Strategie. Die Artefakte können einzeln publiziert werden, was Flexibilität bietet aber Versions-Inkonsistenzen riskiert. Alternativ erfolgt eine koordinierte Publication aller Module mit derselben Version, was Konsistenz garantiert aber weniger granulare Updates ermöglicht. Die Wahl hängt von der Release-Strategie und der Coupling-Stärke zwischen Modulen ab.
Die Publication-Konfiguration erfolgt in jedem Subprojekt, das
Artefakte publizieren soll. Das publishing-Block definiert
Publications und Repositories. Eine Publication spezifiziert die zu
publizierenden Artefakte und deren Metadaten. Die
from-Methode verbindet die Publication mit einer
Software-Component, typischerweise components.java für
Java-Libraries.
// In jedem publizierenden Subprojekt
plugins {
`java-library`
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
// Artifact-Metadaten
artifactId = project.name
// Zusätzliche Artefakte
artifact(tasks.register<Jar>("sourcesJar") {
from(sourceSets.main.get().allJava)
archiveClassifier.set("sources")
})
// POM-Anpassungen
pom {
name.set("${project.group}:${project.name}")
description.set("Komponente ${project.name} des Systems")
url.set("https://github.com/company/project")
licenses {
license {
name.set("Apache License 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0")
}
}
developers {
developer {
id.set("team")
name.set("Development Team")
email.set("dev@company.com")
}
}
}
}
}
}Die Zentralisierung gemeinsamer Publication-Konfiguration reduziert
Redundanz. Ein Convention-Plugin im buildSrc definiert
Standard-Publications, die von allen Subprojekten verwendet werden.
Projekt-spezifische Anpassungen überschreiben oder erweitern diese
Standards. Diese Struktur garantiert konsistente Metadaten über alle
publizierten Artefakte.
Inter-Modul-Dependencies in Multi-Projekt-Builds werden zu
Maven-Dependencies in publizierten POMs transformiert. Gradle mappt
project()-Dependencies automatisch zu Maven-Koordinaten
basierend auf Group und Artifact-ID der referenzierten Module. Diese
Transformation preserviert die Dependency-Struktur für externe
Konsumenten.
Die Dependency-Scope-Zuordnung folgt klaren Regeln. Gradle’s
implementation-Dependencies werden zu Maven’s
runtime-Scope, während api-Dependencies zu
compile-Scope werden. Test-Dependencies landen im
test-Scope. Diese Mappings können durch explizite
Konfiguration überschrieben werden, wenn Maven-Konsumenten spezielle
Scope-Requirements haben.
dependencies {
api(project(":core")) // -> compile scope
implementation(project(":commons")) // -> runtime scope
testImplementation(project(":test-utils")) // -> test scope
}
// Explizite Scope-Kontrolle
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
pom.withXml {
val dependenciesNode = asNode().appendNode("dependencies")
configurations["implementation"].allDependencies.forEach {
if (it.group != null) {
val dependencyNode = dependenciesNode.appendNode("dependency")
dependencyNode.appendNode("groupId", it.group)
dependencyNode.appendNode("artifactId", it.name)
dependencyNode.appendNode("version", it.version)
dependencyNode.appendNode("scope", "provided") // Custom scope
}
}
}
}
}
}Platform- und BOM-Publications koordinieren Versions-Management über Module. Ein dediziertes Platform-Modul definiert Dependency-Constraints für alle Module des Multi-Projekt-Builds. Konsumenten können diese Platform importieren und erhalten automatisch kompatible Versionen aller Module. Diese Strategie entspricht dem Spring-Boot-BOM-Pattern und vereinfacht Dependency-Management für Nutzer.
Die Versionierung in Multi-Projekt-Builds folgt verschiedenen Strategien. Monolithische Versionierung verwendet dieselbe Version für alle Module, vereinfacht durch eine zentrale Definition im Root-Projekt. Diese Strategie garantiert Kompatibilität zwischen Modulen und vereinfacht Release-Management. Der Nachteil liegt in unnötigen Versions-Updates für unveränderte Module.
// Root build.gradle.kts
allprojects {
group = "com.company.system"
version = "2.3.0"
}
// Alternativ: Aus external source
val systemVersion: String by project // from gradle.properties
allprojects {
version = systemVersion
}Independent Module Versioning erlaubt unterschiedliche Versions-Nummern pro Modul. Diese Strategie ermöglicht granulare Updates und semantic versioning pro Modul. Die Komplexität liegt in der Verwaltung kompatibler Versions-Kombinationen. Eine Compatibility-Matrix dokumentiert, welche Modul-Versionen zusammen funktionieren. Automated Testing verifiziert diese Kombinationen.
Snapshot- und Release-Versionen erfordern unterschiedliche Publication-Strategien. Snapshot-Versionen werden zu Snapshot-Repositories publiziert und bei jedem Build überschrieben. Release-Versionen sind immutable und werden zu Release-Repositories publiziert. Das Build-Skript unterscheidet basierend auf der Version und wählt das entsprechende Repository.
Die Repository-Konfiguration definiert, wohin Artefakte publiziert werden. Typische Setups unterscheiden zwischen lokalen Repositories für Development, unternehmensinternen Repositories für Team-Sharing und öffentlichen Repositories für Open-Source-Projekte. Jedes Repository hat eigene URL-, Authentication- und Policy-Requirements.
publishing {
repositories {
maven {
name = "corporate"
url = uri(
if (version.toString().endsWith("SNAPSHOT"))
"https://nexus.company.com/repository/maven-snapshots"
else
"https://nexus.company.com/repository/maven-releases"
)
credentials {
username = project.findProperty("nexusUsername") as String?
?: System.getenv("NEXUS_USERNAME")
password = project.findProperty("nexusPassword") as String?
?: System.getenv("NEXUS_PASSWORD")
}
// Repository-spezifische Konfiguration
isAllowInsecureProtocol = false
authentication {
create<BasicAuthentication>("basic")
}
}
// Lokales Repository für Testing
maven {
name = "local"
url = uri("${buildDir}/repo")
}
}
}Credential-Management erfolgt außerhalb der Build-Skripte. Sensitive Daten wie Passwörter oder API-Keys werden über Gradle-Properties, Umgebungsvariablen oder Secret-Management-Systeme bereitgestellt. In CI/CD-Pipelines kommen verschlüsselte Secrets oder temporäre Credentials zum Einsatz. Die Build-Skripte referenzieren diese Credentials ohne sie zu exponieren.
Multi-Repository-Strategien unterstützen verschiedene Deployment-Szenarien. Development-Builds publizieren zu internen Repositories, während Releases zu öffentlichen Repositories gehen. Geographic Distribution nutzt regionale Repositories für bessere Performance. Repository-Gruppen in Nexus oder Artifactory aggregieren mehrere Repositories zu einer einheitlichen Sicht.
Aggregierte Publications kombinieren mehrere Module zu einem Release-Bundle. Diese Bundles enthalten alle Artefakte einer Version mit koordinierten Metadaten. Ein dediziertes Distribution-Modul sammelt die Artefakte aller Subprojekte und erstellt Archive oder Container-Images für Deployment.
// distribution/build.gradle.kts
plugins {
base
`maven-publish`
}
val collectArtifacts by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
}
dependencies {
subprojects.filter { it.plugins.hasPlugin("maven-publish") }.forEach {
collectArtifacts(project(it.path))
}
}
val distributionZip = tasks.register<Zip>("distributionZip") {
from(collectArtifacts)
archiveBaseName.set("system-distribution")
destinationDirectory.set(layout.buildDirectory.dir("distributions"))
}
publishing {
publications {
create<MavenPublication>("distribution") {
artifact(distributionZip)
artifactId = "system-distribution"
pom {
packaging = "zip"
name.set("Complete System Distribution")
// Referenziert alle enthaltenen Module
withXml {
val deps = asNode().appendNode("dependencies")
subprojects.filter { it.plugins.hasPlugin("maven-publish") }.forEach {
val dep = deps.appendNode("dependency")
dep.appendNode("groupId", it.group)
dep.appendNode("artifactId", it.name)
dep.appendNode("version", it.version)
}
}
}
}
}
}Bill of Materials (BOM) Publications definieren Dependency-Management
ohne eigene Artefakte. Das BOM-POM enthält nur
dependencyManagement-Sections mit Versions-Constraints für
alle Module. Konsumenten importieren das BOM und erhalten konsistente
Versionen ohne explizite Versions-Angaben. Diese Strategie reduziert
Versions-Konflikte und vereinfacht Updates.
Signing und Verification sichern die Integrität publizierter Artefakte. Das Signing-Plugin generiert GPG-Signaturen für alle Artefakte. Repository-Manager verifizieren diese Signaturen und rejekten unsignierte oder falsch signierte Artefakte. Die Signing-Konfiguration unterscheidet zwischen Development-Keys für Snapshots und Production-Keys für Releases. Automated Signing in CI/CD nutzt Hardware Security Modules oder Key Management Services.