How to centralize Gradle build settings?

755 Views Asked by At

Say I'm using the palantir/gradle-git-version Gradle plugin, and have the following code in build.gradle.kts to determine the project version:

// If release branch, return after incrementing patch version.
// Else, return $lastTag-SNAPSHOT.
val projectVersion: String by lazy {
    val versionDetails: groovy.lang.Closure<VersionDetails> by extra
    with(versionDetails()) {
        if (!lastTag.matches("^(?:(?:\\d+\\.){2}\\d+)\$".toRegex())) {
            throw GradleException("Tag '$lastTag' doesn't match 'MAJOR.MINOR.PATCH' format")
        }
        // If it detached state, get branch name from GitLab CI env var
        val branch = branchName ?: System.getenv("CI_COMMIT_REF_NAME")
        if (branch?.startsWith("release/") == true) {
            val tokens = lastTag.split('.')
            "${tokens[0]}.${tokens[1]}.${tokens[2].toInt() + commitDistance}"
        } else "$lastTag-SNAPSHOT"
    }
}

This works, but the code is duplicated across all the projects, which is difficult to maintain except for a very small number of projects.

This is just one example, the same applies for other Gradle tasks that assume certain conventions within the company/team, like creating a Dockerfile.

What is a good way to centralize such code so that all projects can use them? Note that code like this don't usually stand on their own, but rely on Gradle plugins.

1

There are 1 best solutions below

4
On

What is a good way to centralize such code so that all projects can use them?

You'll want to create a custom Gradle plugin to hold your project's conventions.

If you have Gradle installed locally, you can use the Build Init Plugin to create a skeleton plugin project. With Gradle installed locally, simple run gradle init in a new project directory and follow the prompts to create the plugin project.

As a concrete example (assuming you generated a plugin project as mentioned earlier), to apply your versioning conventions, a plugin could be:

// Plugin's build.gradle.kts
dependencies {
    // Add dependency for plugin, GAV can be found on the plugins page:
    // https://plugins.gradle.org/plugin/com.palantir.git-version
    implementation("com.palantir.gradle.gitversion:gradle-git-version:0.12.3")
}

Then a versioning conventions plugin could be:

import com.palantir.gradle.gitversion.VersionDetails
import groovy.lang.Closure
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project

class VersioningConventionsPlugin : Plugin<Project> {

    override fun apply(project: Project) {
        // Apply plugin to project as you would in the main Gradle build file.
        project.pluginManager.apply("com.palantir.git-version")

        // Configure version conventions
        val projectVersion: String by lazy {

            // Gradle generates some Kotlin DSL code on the fly, in a plugin implementation we don't have that.
            // So we must convert the DSL to the Gradle API.
            val versionDetails: Closure<VersionDetails> = project.extensions.extraProperties.get("versionDetails") as Closure<VersionDetails>

            with(versionDetails.call()) {
                if (!lastTag.matches("^(?:(?:\\d+\\.){2}\\d+)\$".toRegex())) {
                    throw GradleException("Tag '$lastTag' doesn't match 'MAJOR.MINOR.PATCH' format")
                }
                val branch = branchName ?: System.getenv("CI_COMMIT_REF_NAME")
                if (branch?.startsWith("release/") == true) {
                    val tokens = lastTag.split('.')
                    "${tokens[0]}.${tokens[1]}.${tokens[2].toInt() + commitDistance}"
                } else "$lastTag-SNAPSHOT"
            }
        }

        // Set the version as an extra property on the project
        // Accessible via extra["projectVersion"]
        project.extensions.extraProperties["projectVersion"] = projectVersion
    }
}

I gave a Kotlin example since your sample used the Kotlin DSL. Once you've finished development work of your conventions plugin, then you would publish to a repository such as the Gradle Plugins repository. If it's an internal company plugin, then publish it to an internal Nexus Repository or similar.

Follow the docs for the maven-publish plugin for more details on publishing. Gradle plugins can be published like any other artifact/JAR.