Search

Dark theme | Light theme

March 26, 2023

Gradle Goodness: Publish Version Catalog For Sharing Between Projects

A version catalog in Gradle is a central place in our project where we can define dependency references with their version or version rules. We can define a version catalog using an API in our build file, but we can also create an external file where we define our dependencies and version. In our dependencies section we can refer to the names in the version catalog using a type-safe accessor (if we use Kotlin for writing our build script) with code completion in a supported IDE (IntelliJ IDEA). If we want to share a version catalog between projects we can publish a version catalog to a Maven repository with a groupId, artifactId and version.

In order to do this we need to create a new Gradle project that will only contain the definitions for our version catalog. We must add two gradle plugins: version-catalog and maven-publish. The version-catalog plugin adds a new extension versionCatalog to our build file. Here we define the content of the version catalog we want to share. We can refer to an external version catalog file written in the TOML format that is dictated by Gradle. But we can also use an API provided by VersionCatalogBuilder to define our versions, plugins, libraries and bundles. The publication outcome of this project is a Maven POM file and generated version catalog file in TOML format. Using the maven-publish plugin we can publish our version catalog to a Maven repository. Other projects can then refer to this published version catalog in their Gradle settings file. In the build script we can use the dependencies using the type-safe accessor we alread know for a project version catalog.

In the following example we first look at the Gradle project that has all the data to publish a version catalog. We first create an external version catalog file:

# File: gradle/libs.versions.toml
[versions]
junit5 = "5.9.1"

[libraries]
junit-api = {
    module = "org.junit.jupiter:junit-jupiter-api",
    version.ref = "junit5"
}
junit-engine = {
    module = "org.junit.jupiter:junit-jupiter-engine",
    version.ref = "junit5"
}

helidon-deps = "io.helidon:helidon-dependencies:3.2.0"

We define the rootProject name as this will be the artifact identifier of the version catalog we will publish:

// File: settings.gradle.kts
rootProject.name = "version-catalog"

Finally we have a build file where we define the version catalog and Maven publish plugins. Furthermore we configure our version catalog with data from the external file and we use the API. Lastly we define the publishing configuration:

// File: build.gradle.kts
plugins {
    // Version catalog plugin will add the catalog extension
    // to our build file where we can define the version
    // catalog contents.
    `version-catalog`

    // Maven publish plugin so we can publish our version catalog
    // to a Maven repository.
    `maven-publish`
}

group = "mrhaki.shared"
version = "1.1.0"

// catalog extension added by version-catalog plugin.
catalog {
    versionCatalog {
        // We can refer to an external version catalog file.
        // This could even be a published version catalog as well.
        from(files("gradle/libs.versions.toml"))

        // But also use the methods of the VersionCatalogBuilder.
        library("helidon-deps", "io.helidon:helidon-dependencies:3.2.0")
    }
}

publishing {
    publications {
        create<MavenPublication>("maven") {
            // The version-catalog plugin adds a new component
            // "versionCatalog" that we can use a publication.
            // We will get a POM file and a generated version catalog
            // TOML file that are part of the publication.
            from(components["versionCatalog"])
        }
    }

    repositories {
        // Configuration for Maven repo to publish our version catalog to.
        maven {
            url = uri("https://intranet.repo/repository/maven-releases")

            credentials {
                val mavenRepoUsername: String by project
                val mavenRepoPassword: String by project
                username = mavenRepoUsername
                password = mavenRepoPassword
            }
        }
    }
}

Now we create the Gradle project build files that will use our published version catalog. First we must refer to the published version catalog in our settings file. We define the Maven repository where we published the catalog to and then refer to the artifact to assign it to the sharedLibs version catalog accessor:

// File: settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        // Configuration for Maven repo to get our
        // published version catalog from.
        maven {
            url = uri("https://intranet.repo/repository/maven-public")

            credentials {
                val mavenRepoUsername: String by settings
                val mavenRepoPassword: String by settings
                username = mavenRepoUsername
                password = mavenRepoPassword
            }
        }
    }

    versionCatalogs {
        // We create a new version catalog with the
        // given name sharedLibs.
        // We are free to use any name, Gradle will
        // create Kotlin accessors we can use in our build file.
        create("sharedLibs") {
            from("mrhaki.shared:version-catalog:1.1.0")
        }
    }
}

Now we are all setup and in our build file we can use the type-safe accessors to get the dependencies in our project dependencies section:

// File: build.gradle.kts
plugins {
    java // We want to craete a Java Helidon app.
}

repositories {
    // Repository for downloading the dependencies.
    mavenCentral()
}

dependencies {
    // We can reference sharedLibs.helidon.deps
    // from our shared version catalog.
    implementation(platform(sharedLibs.helidon.deps))

    // List of dependencies for Helidon where the version
    // can be left out as we use platform(libs.helidon.deps)
    // to include our Bill of Materials (BOM).
    implementation("io.helidon.webserver:helidon-webserver")
    implementation("io.helidon.config:helidon-config-yaml")
    implementation("io.helidon.media:helidon-media-jsonp")

    // We can reference sharedLibs.junit.api and
    // sharedLibs.junit.engine from our shared version catalog.
    testImplementation(sharedLibs.junit.api)
    testImplementation(sharedLibs.junit.engine)

    testImplementation("io.helidon.webclient:helidon-webclient")
}

Written with Gradle 8.0.2