Gradle

To make it quick Gradle is an open source build tool. They moved from groovy to kotlin dsl as their main language, both have been there for multiple version, the real change is that the documentation will be oriented toward the kotlin examples first. I would recommend the kotlin version too, however in 2023, it could still be possible find some edge case where groovy could be slightly more performant on complex build.

Why not just use Maven? 🤪

In my opinion one of the advantages of gradle over Maven are mainly thanks to the syntax, the xml of the maven build files can be really bulky and hard to maintain, whereas the groovy or kotlin syntax of gradle is much more human-readable and maintainable. The build “manifest” is not crowded with uninformative tags… less code for more logic is something I appreciate.

Cheat sheet

In this article, I wanted to sum up some quick tips with gradle, and keep up with the newest syntax of the latest version. Since I have already went through some specific use case, I might skip or not go into too many details, so if you are interested check out those articles:

And now let’s proceed, get your build.gradle.kts ready!

Wrapper

The Gradle wrapper is a script that invokes a declared version of Gradle, downloading it beforehand if necessary as per the documentation. It’s a foundation for your project, to make it easier to build it consistently. Create the gradle wrapper to call ./gradlew in the version you want with:

gradle wrapper --gradle-version 8.2.1

That’s useful when working with legacy projects that may use older gradle version, or if you want to set a working gradle version in your project.

Dependencies

To show the project dependencies use:

./gradlew dependencies
./gradlew dependencies --configuration <configuration>

The second one is to check the dependency tree for a specific configuration like compileClasspath or kotlinCompilerClasspath for kotlin projects.

Add dependencies

To add a dependency, you need to add a repository from where to fetch it and the actual dependency:

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

The compile and testCompile has been deprecated. For more info, check the gradle documentation.

Exclude dependencies

This is to either avoid conflicts with clashing dependencies or for any reason you wouldn’t want a specific dependency in your project. To exclude a dependency from your build, from all dependency use:

configurations.all {
  exclude(group= "org.group", module= "conflicting-lib")
}

To exclude from a specific configuration (here testImplementation), you can use:

configurations.testImplementation {
    exclude(group = "junit", module = "junit")
}

Why excluding junit 4 in this example? Because I don’t want a mix of junit 4 and 5 in my project. And if the problem is only in one dependency you can also exclude it from there only using:

testImplementation("org.springframework.boot:spring-boot-starter-test") {
    exclude(module = "junit")
    exclude(module="junit-vintage-engine")
}

The exclude function is called in the closure of the testImplementation configuration for the spring-boot-starter-test dependency.

Multi project dependency

For your multi project build, you can follow the gradle documentation. And to add a subproject as a dependency of another project, you can check the stackoverflow answer.

Publishing

To publish, in a maven repository you will need the maven-publish plugin. Then you will need to add the publishing script in your build.gradle.kts:

plugins {
    `maven-publish`
}

publishing {
    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/OWNER/REPOSITORY")
            credentials {
                username = 'username'
                password = 'password'
            }
        }
    }
    publications {
        create<MavenPublication>("gpr") {
            from(components["java"])
        }
    }
}

group="com.github.OWNER"
version="1.0-SNAPSHOT"

Using properties

Properties are useful to avoid hardcoding values in your build file (e.g. passwords), to add one you can use:

val gprUser: String? by project
val gprApiKey: String? by project

credentials {
    username = gprUser
    password = gprApiKey
}

Then you can pass the properties via command line on publish

./gradlew publish -PgprUser="username" -PgprApiKey="password"

You can also save some variable in a gradle.properties file in your project root directory:

gprUser=username

There are other ways to pass the variables, with environment variables or without declared variables:

password = project.findProperty("gprApiKey") as String? ?: System.getenv("GPR_API_KEY")

Tests

Using junit 5, you can improve your testing experience with gradle:

dependencies {
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
    testImplementation("org.junit.jupiter:junit-jupiter-api")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}

Then to run your test, you can use the junit engine using useJUnitPlatform in your test task:

tasks.test {
    useJUnitPlatform()
    testLogging {
        showStandardStreams = true
        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
        events("skipped", "failed")
    }
    maxParallelForks = 3
}

The maxParallelForks is to run your tests in parallel, but it can’t be higher than your number of cores. The testLogging is optional as well to log the tests output in the terminal.