Skip to content

Commit

Permalink
Publish a version catalog alongside the BOM
Browse files Browse the repository at this point in the history
This commit introduces a new artifact published alongside the BOM:
a Gradle _version catalog_. A version catalog is not a replacement
for a BOM. It can be seen as an alternative, or a complement to a
BOM file.

For example, a user can import the Micronaut version catalog in
their settings file by doing:

```gradle
dependencyResolutionManagement {
    versionCatalogs {
        create("mn") {
            from("io.micronaut:micronaut-bom:3.0.1-SNAPSHOT")
        }
    }
}
```

Gradle will then generate _version accessors_, which allows
replacing dependency notations from:

```gradle
implementation "ch.qos.logback:logback-classic"
```

to:

```gradle
implementation mn.logback
```

Those accessors are _type safe_. Version catalogs should
really be seen as "recommendations". Without applying a
BOM in addition, the versions declared in a catalog have
_no impact_ on dependency resolution. Therefore, it is
often recommended to apply both the BOM _and_ use catalogs
to declare dependencies.

It comes with an advantage, which is that versions declared
in the BOM can be overridden by the user:

```gradle
dependencyResolutionManagement {
    versionCatalogs {
        create("mn") {
            from("io.micronaut:micronaut-bom:3.0.1-SNAPSHOT")
            version("snakeyaml") {
                strictly("1.28")
            }
        }
    }
}
```
  • Loading branch information
melix committed Sep 2, 2021
1 parent cb96690 commit 6545bc7
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 11 deletions.
48 changes: 37 additions & 11 deletions bom/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import io.micronaut.build.internal.pom.VersionCatalogConverter
import org.gradle.api.plugins.catalog.CatalogPluginExtension
import org.gradle.api.plugins.catalog.VersionCatalogPlugin

plugins {
id 'java-platform'
id 'version-catalog'
id 'maven-publish'
id 'io.micronaut.build.internal.publishing'
}

group projectGroupId
version projectVersion


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

def excludedProjects = [
"benchmarks",
"inject-test-utils",
Expand All @@ -20,7 +22,7 @@ def excludedProjects = [
"test-utils"
]

def catalog = project.extensions.findByType(VersionCatalogsExtension).named("libs")
def libsCatalog = project.extensions.findByType(VersionCatalogsExtension).named("libs")

// This map defines the names of the properties found in the POM file
// which cannot be directly inferred from the version name in the catalog.
Expand All @@ -39,15 +41,31 @@ String toPropertyName(String alias) {
alias.split("(?=[A-Z])").collect { it.toLowerCase(Locale.US) }.join("-").replace((char)'-', (char)'.')
}

components.javaPlatform.addVariantsFromConfiguration(
configurations.getByName(VersionCatalogPlugin.VERSION_CATALOG_ELEMENTS)
) { details ->
details.mapToMavenScope("compile")
details.mapToOptional()
}

def modelConverter = new VersionCatalogConverter(
file("../gradle/libs.versions.toml"),
project.extensions.findByType(CatalogPluginExtension)
)

tasks.named("generateCatalogAsToml") {
modelConverter.populateModel()
}

publishing {
publications {
publication(MavenPublication) {
artifactId("micronaut-bom")
from components.javaPlatform

catalog.versionAliases.each { alias ->
libsCatalog.versionAliases.each { alias ->
if (alias.startsWith('managed.')) {
catalog.findVersion(alias).ifPresent { version ->
libsCatalog.findVersion(alias).ifPresent { version ->
alias = alias - 'managed.'
String baseName = legacyVersionNames[alias] ?: toPropertyName(alias)
String propertyName = "${baseName}.version"
Expand All @@ -64,9 +82,9 @@ javaPlatform {
}

dependencies {
catalog.dependencyAliases.each { alias ->
libsCatalog.dependencyAliases.each { alias ->
if (alias.startsWith("boms.")) {
api platform(catalog.findDependency(alias).map {
api platform(libsCatalog.findDependency(alias).map {
it.get()
}.orElseThrow { new RuntimeException("Unexpected missing alias in catalog") })
}
Expand All @@ -84,12 +102,20 @@ dependencies {
continue
}

api "$p.group:micronaut-$p.name:$p.version"
String moduleGroup = p.group
String moduleName = "micronaut-${p.name}"
String moduleVersion = p.version

api "$moduleGroup:$moduleName:$moduleVersion"

modelConverter.extraVersions.put(moduleName, moduleVersion)
modelConverter.extraLibraries.put(moduleName, VersionCatalogConverter.library(moduleGroup, moduleName, moduleName))

}

catalog.dependencyAliases.each { alias ->
libsCatalog.dependencyAliases.each { alias ->
if (alias.startsWith("managed.")) {
api catalog.findDependency(alias).map {
api libsCatalog.findDependency(alias).map {
it.get()
}.orElseThrow { new RuntimeException("Unexpected missing alias in catalog") }
}
Expand Down
9 changes: 9 additions & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
plugins {
id 'groovy-gradle-plugin'
}

repositories {
gradlePluginPortal()
}

dependencies {
implementation "io.micronaut.build.internal:micronaut-gradle-plugins:4.1.0-SNAPSHOT"
implementation "org.tomlj:tomlj:1.0.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.micronaut.build.internal.pom

import groovy.transform.Canonical
import io.micronaut.build.catalogs.internal.LenientVersionCatalogParser
import org.gradle.api.plugins.catalog.CatalogPluginExtension
import org.gradle.api.tasks.TaskAction
/**
* This is an internal task which responsibility is to parse
* the Micronaut version catalog used internally, extract components
* which belong to the BOM, in order to populate the version catalog
* model for Gradle.
*
* In the end, this model is used to generate a version catalog in
* addition to the bom. This will let users choose between importing
* a BOM or using the Micronaut version catalog.
*
*/
@Canonical
class VersionCatalogConverter {
final File catalogFile
final CatalogPluginExtension catalogExtension
final Map<String, String> extraVersions = [:]
final Map<String, Library> extraLibraries = [:]

@TaskAction
void populateModel() {
def parser = new LenientVersionCatalogParser()
parser.parse(catalogFile.newInputStream())
def model = parser.model
catalogExtension.versionCatalog {builder ->
extraVersions.forEach { alias, version ->
builder.version(alias, version)
}
extraLibraries.forEach { alias, library ->
builder.alias(alias)
.to(library.group, library.name)
.versionRef(library.versionRef)
}
model.versionsTable.each { version ->
if (version.reference.startsWith('managed-')) {
builder.version(version.reference.substring(8), version.version.require)
}
}
model.librariesTable.each { library ->
if (library.alias.startsWith("managed-") && library.version.reference) {
builder.alias(library.alias.substring(8))
.to(library.group, library.name)
.versionRef(library.version.reference.substring(8))
}
}
}
}

static Library library(String group, String name, String versionRef) {
new Library(group, name, versionRef)
}

@Canonical
static class Library {
final String group
final String name
final String versionRef
}
}

0 comments on commit 6545bc7

Please sign in to comment.