mrkuz / kradle

Swiss army knife for Kotlin/JVM (and also Java) development
MIT License
83 stars 2 forks source link
gradle gradle-plugin kotlin

Kradle

Gradle Plugin Portal

Swiss army knife for Kotlin/JVM (and also Java) development.

kradle is a Gradle plugin, which sets up your Kotlin/JVM (or Java) project in no time.

With a few lines of configuration, you will be able to:

Most of the functionality is provided by other well-known plugins. kradle takes care of the setup and provides a unified configuration DSL.

Table of contents

What's new?

See CHANGELOG.

(Very) Quick Start

Kotlin:

mkdir demo && cd demo
curl -O https://raw.githubusercontent.com/mrkuz/kradle/main/examples/kotlin/app/settings.gradle.kts
curl -O https://raw.githubusercontent.com/mrkuz/kradle/main/examples/kotlin/app/build.gradle.kts
gradle bootstrap

Java:

mkdir demo && cd demo
curl -O https://raw.githubusercontent.com/mrkuz/kradle/main/examples/java/app/settings.gradle.kts
curl -O https://raw.githubusercontent.com/mrkuz/kradle/main/examples/java/app/build.gradle.kts
gradle bootstrap

Run application:

./gradlew run

Package application and run JAR:

./gradlew uberJar && java -jar build/libs/demo-1.0.0-uber.jar

Build Docker image and run container:

./gradlew buildImage && docker run --rm demo

Quick Start

Add the kradle plugin to your build script: net.bitsandbobs.kradle.

build.gradle.kts

plugins {
    id("org.jetbrains.kotlin.jvm") version "1.9.10"
    id("net.bitsandbobs.kradle") version "main-SNAPSHOT"
}

group = "com.example"
version = "1.0.0"

kradle {
    kotlinJvmApplication {
        jvm {
            application {
                mainClass("com.example.demo.AppKt")
            }
        }
    }
}

Make sure you apply the Kotlin plugin before kradle. For applications, you have to provide the mainClass.

If you are starting from scratch, you can run gradle boostrap to initialize Git, add Gradle wrapper and create essential directories and files.

The example above uses the Kotlin/JVM application preset.

Check the configuration reference to see all available options.

For Java projects apply the Java plugin instead of the Kotlin plugin.

Tasks

Which tasks are available, depends on the features enabled.

Task Description Alias for Plugins used
bootstrap Bootstraps app/lib project - -
showDependencyUpdates Displays dependency updates - Gradle Versions Plugin
lint Runs ktlint (Kotlin) and checkstyle (Java) - ktlint Plugin, Checkstyle Plugin
analyzeCode Runs detekt (Kotlin), PMD (Java) and SpotBugs (Java) code analysis - detekt Plugin, PMD Plugin, SpotBugs Plugin
analyzeDependencies Analyzes dependencies for vulnerabilities - OWASP Dependency Check Plugin
dev Runs the application and stops it when sources change (use with -t, applications only) - -
runBenchmarks Runs JMH benchmarks benchmark kotlinx.benchmark Plugin
integrationTest Runs integration tests - -
functionalTest Runs functional tests - -
runTests Runs all tests - -
analyzeTestCoverage Runs test coverage analysis - Kover, JaCocCo Plugin
generateDocumentation Generates Dokka HTML documentation - Dokka Plugin
package Creates JAR jar Java Plugin
uberJar Creates Uber-JAR (applications only) - Gradle Shadow Plugin
buildImage Builds Docker image (applications only) - Jib Plugin
pushImage Pushes container image to remote registry (applications only) - Jib Plugin
install Installs JAR to local Maven repository (libraries only) publishToMavenLocal Maven Publish Plugin
generateGitignore Generates .gitignore - -
generateBuildProperties Generates build.properties - -
generateDetektConfig Generates detekt-config.yml - -
generateCheckstyleConfig Generates checkstyle.xml - -
generateLombokConfig Generates lombok.config - -
generateLog4jConfig Generates log4j.xml - -
generateHelmChart Generates Helm chart - -
processHelmChart Processes Helm chart - -
compile Compiles main classes classes -
verify Runs all checks and tests check -
kradleDump Dumps kradle diagnostic information - -

Features

kradle groups its functionality into features. They must be enabled explicitly. For example to get support for benchmarks:

kradle {
    jvm {
        benchmark.enable()
    }
}

You can use one of the following statements, whatever fits your taste.

benchmark.enable()
benchmark(true)
benchmark()

If the feature has options, enable takes a configuration code block as argument.

kradle {
    jvm {
        benchmark.enable {
            jmh {
                version("1.37")
            }
        }
    }
}

This configures and enables the feature. You can omit enable and use benchmark { … }. To configure the feature without enabling it, use configureOnly.

It is also possible to disable features. This can be useful if you are using presets and want to get rid of inherited features.

benchmark.disable()
benchmark(false)

Options shown in this section of the documentation represent the defaults.

If the name of the option starts with use, it adds dependencies to your project (e.g. useKotest).

Features can have sub-features. For example, junitJupiter is a sub-feature of test.

kradle {
    jvm {
        test {
            junitJupiter.enable {
                version("5.10.0")
            }
        }
    }
}

In contrast to normal features, some of them are enabled per default.

General

kradle {
    general {
        …
    }
}

Groups general features.

Bootstrapping

kradle {
    general {
        bootstrap.enable()
    }
}

Adds the task bootstrap, which

Git integration

kradle {
    general {
        git.enable()
    }
}

Adds the task generateGitignore, which generates .gitignore with sane defaults.

gitCommit, gitBranch and gitBranchPrefix are added to the project properties. The gitBranchPrefix is the branch name up to the first occurrence of /, - or _.

Build profiles

kradle {
    general {
        buildProfiles.enable()
    }
}

Adds profile to the project properties.

Options

kradle {
    general {
        buildProfiles {
            active("default")
        }
    }
}

Example

If you want to pass the profile via command line argument (./gradlew -Pprofile=<PROFILE>), you can use following snippet:

kradle {
    general {
        buildProfiles {
            active(project.properties["profile"].toString())
        }
    }
}

Project properties

kradle {
    general {
        projectProperties.enable()
    }
}

Looks for a file called project.properties in the project directory. If found, the entries are added to the project properties.

If build profiles are enabled, the entries of project-<PROFILE>.properties are also added. They have precedence over project.properties.

Build properties

kradle {
    general {
        buildProperties.enable()
    }
}

Adds the task generateBuildProperties, which generates a file build.properties containing the project name, group, version and the build timestamp.

If build profiles are enabled, the active profile is added.

If Git integration is enabled, the Git commit id is added.

The task is executed after processResources.

project.name=…
project.group=…
project.version=…
build.profile=…
build.timestamp=…
git.commit-id=…

Custom scripts

kradle {
    general {
        scripts.enable()
    }
}

Creates new script tasks which execute a chain of shell commands.

Options

kradle {
    general {
        scripts {
            "<NAME>" {
                description("…")
                dependsOn("…")
                prompt(key = "…", text = "…", default = "…")
                commands("…")
            }
        }
    }
}

Example

kradle {
    general {
        scripts {
            "release" {
                description("Create release branch and tag")
                prompt(key = "version", text = "Version?", default = project.version.toString())
                commands(
                    "git checkout -b release/$#{inputs.version}",
                    "git tag v$#{inputs.version}"
                )
            }
        }
    }
}

Adds the task release which can be called like any other task: ./gradlew release.

Helm charts

kradle {
    general {
        helm.enable()
    }
}

Adds the task generateHelmChart, which generates a basic Helm chart in src/main/helm.

Adds the task processHelmChart, which copies src/main/helm to build/helm and expands all property references in Chart.yaml and values.yaml.

Adds following script tasks:

Options

kradle {
    general {
        helm {
            releaseName(project.name)
            // valuesFile("…")
        }
    }
}

JVM features

kradle {
    jvm {
        …
    }
}

Groups JVM related features.

Options

kradle {
    jvm {
        targetJvm("17")
    }
}

Kotlin development

kradle {
    jvm {
        kotlin.enable()
    }
}

Adds Kotlin Standard Library, Kotlin reflection library, and kotlin.test library dependencies.

Enables Opt-ins.

JSR-305 nullability mismatches are reported as error ("-Xjsr305=strict").

Plugins used: kotlinx.serialization Plugin, All-open Compiler Plugin, Java Plugin, detekt Plugin, ktlint Plugin

Sub-features

Options

kradle {
    jvm {
        kotlin {
            // useCoroutines("1.7.3")
            lint {
                ktlint.enable {
                    version("1.0.1")
                    rules {
                        // disable("…")
                    }
                }
            }
            codeAnalysis {
                detekt.enable {
                    version("1.23.3")
                    configFile("detekt-config.yml")
                }
            }
            test {
                // useKotest("5.7.2")
                // useMockk("1.13.8")
            }
        }
    }
}

Java development

kradle {
    jvm {
        java.enable()
    }
}

Plugins used: Java Plugin, PMD Plugin, SpotBugs Plugin, Checkstyle Plugin

Sub-features

Options

kradle {
    jvm {
        java {
            previewFeatures(false)
            // withLombok("1.18.30")
            lint {
                checkstyle.enable {
                    version("10.12.4")
                    configFile("checkstyle.xml")
                }
            }
            codeAnalysis {
                pmd.enable {
                    version("6.55.0")
                    ruleSets {
                        bestPractices(false)
                        codeStyle(false)
                        design(false)
                        documentation(false)
                        errorProne(true)
                        multithreading(true)
                        performance(true)
                        security(true)
                    }
                }
                spotBugs.enable {
                    version("4.8.0")
                    // useFbContrib(7.6.0)
                    // useFindSecBugs(1.12.0)
                }
            }
        }
    }
}

Application development

kradle {
    jvm {
        application.enable()
    }
}

Conflicts with library development.

If build profiles are enabled, the environment variable KRADLE_PROFILE is set when using run.

Plugins used: Application Plugin

Options

kradle {
    jvm {
        application {
            // mainClass("…")
        }
    }
}

Library development

kradle {
    jvm {
        library.enable()
    }
}

Adds the task install, which installs the library to your local Maven repository.

Conflicts with application development.

Plugins used: Java Library Plugin , Maven Publish Plugin

Dependency management

kradle {
    jvm {
        dependencies.enable()
    }
}

Adds the task showDependencyUpdates, which shows all available dependency updates. It only considers stable versions; no alpha, beta, release candidate or milestone builds.

Plugins used: Gradle Versions Plugin

Options

kradle {
    jvm {
        dependencies {
            useCaffeine("3.1.8")
            useGuava("32.1.3-jre")
            useLog4j("2.21.1")
        }
    }
}

Vulnerability scans

kradle {
    jvm {
        vulnerabilityScan.enable()
    }
}

Adds the task analyzeDependencies, which scans all dependencies on the runtime and compile classpath for vulnerabilities.

Plugins used: OWASP Dependency Check Plugin

Linting

kradle {
    jvm {
        lint.enable()
    }
}

Adds the task lint, which runs enabled linters.

lint is executed when running check.

Options

kradle {
    jvm {
        lint {
            ignoreFailures(false)
        }
    }
}

Code analysis

kradle {
    jvm {
        codeAnalysis.enable()
    }
}

Adds the task analyzeCode, which runs enabled code analysis tools:

analyzeCode is executed when running check.

Options

kradle {
    jvm {
        codeAnalysis {
            ignoreFailures(false)
        }
    }
}

Development mode

kradle {
    jvm {
        developmentMode.enable()
    }
}

Adds the task dev, which watches the directories src/main/kotlin, src/main/java and src/main/resource. If changes are detected, the application is stopped. Should be used with continuous build flag -t to archive automatic rebuilds and restarts.

When launching the application with dev, the environment variable KRADLE_DEV_MODE=true is set.

If build profiles are enabled, the environment variable KRADLE_PROFILE is set.

To speed up application start, the JVM flag -XX:TieredStopAtLevel=1 is used.

Requires application development.

Test improvements

kradle {
    jvm {
        test.enable()
    }
}

Test file names can end with Test, Tests, Spec or IT.

When running tests, the environment variables KRADLE_PROJECT_DIR and KRADLE_PROJECT_ROOT_DIR are set.

If build profiles are enabled, the environment variable KRADLE_PROFILE is set.

Adds the task runTests, which runs all tests (unit, integration, functional, custom).

Plugins used: Java Plugin , Gradle Test Logger Plugin

Sub-features

Options

kradle {
    jvm {
        test {
            junitJupiter.enable {
                version("5.10.0")
            }
            prettyPrint(false)
            showStandardStreams(false)
            withIntegrationTests(false)
            withFunctionalTests(false)
            /*
            withCustomTests("<NAME>")
            useArchUnit("1.1.0")
            useTestcontainers("1.19.1")
            */
        }
    }
}

Code coverage

kradle {
    jvm {
        codeCoverage.enable()
    }
}

Adds the task analyzeTestCoverage, which runs enabled test coverage tools.

analyzeTestCoverage is executed when running check.

Plugins used: Kover, JaCocCo Plugin

Sub-features

Options

kradle {
    jvm {
        codeCoverage {
            kover {
                excludes("…")
            }
            /*
            jacoco.enable {
                version("0.8.11")
                excludes("…")
            }
            */
        }
    }
}

Benchmarks

kradle {
    jvm {
        benchmark.enable()
    }
}

Adds the task runBenchmarks, which runs JMH benchmarks found under src/benchmark/kotlin.

Plugins used: kotlinx.benchmark Plugin , All-open Compiler Plugin

Options

kradle {
    jvm {
        benchmark {
            jmh {
                version("1.37")
            }
        }
    }
}

Packaging

kradle {
    jvm {
        packaging.enable()
    }
}

Adds the task uberJar, which creates an Uber-Jar. This is a JAR containing all dependencies.

Adds the task package, which is an alias for jar.

Adds Main-Class the manifest, so the JAR is runnable (application only).

Plugins used: Gradle Shadow Plugin

Options

kradle {
    jvm {
        packaging {
            uberJar {
                minimize(false)
            }
        }
    }
}

Docker

kradle {
    jvm {
        docker.enable()
    }
}

Adds the task buildImage, which creates a Docker image using Jib.

Adds the task pushImage, which pushes the container image to remote registry.

Adds the project property imageName.

Files in src/main/extra/ will be copied to the image directory /app/extra/.

Plugins used: Jib Plugin

Requires application development.

Options

kradle {
    jvm {
        docker {
            baseImage("bellsoft/liberica-openjdk-alpine:17")
            imageName(project.name)
            allowInsecureRegistries(false)
            // ports(…)
            // jvmOptions("…")
            // arguments("…")
            // withJvmKill(1.16.0")
            withStartupScript(false)
        }
    }
}

Documentation

kradle {
    jvm {
        documentation.enable()
    }
}

Adds the task generateDocumentation, which uses Dokka to generate a HTML documentation based on KDoc comments. The documentation can be found under build/docs.

Package and module documentation can be placed in package.md or module.md in the project or any source directory.

Plugins used: Dokka Plugin

Logging

kradle {
    jvm {
        logging.enable()
    }
}

Options

kradle {
    jvm {
        logging {
            withSlf4j("2.0.9")
            withLog4j("2.21.1")
        }
    }
}

Frameworks

kradle {
    jvm {
        frameworks {
            …
        }
    }
}

Configures miscellaneous frameworks.

The integration of frameworks is very basic. For more control and features consider using their official Gradle plugins.

Spring Boot

kradle {
    jvm {
        frameworks {
            springBoot.enable()
        }
    }
}

Adds Spring Boot and Spring Boot Test dependencies.

If build profiles are enabled, the environment variable SPRING_PROFILE_ACTIVE is set when using run and dev.

Options

kradle {
    jvm {
        frameworks {
            springBoot {
                version("3.1.5")
                // withDevTools("3.1.5")
                // useWeb("3.1.5")
                // useWebFlux("3.1.5")
                // useActuator("3.1.5")
            }
        }
    }
}

Presets

Presets preconfigure kradle for specific use cases. The options set by the preset can be overridden.

Example:

kradle {
    kotlinJvmLibrary {
        jvm {
            buildProperties.disable()
        }
    }
}

Kotlin/JVM application

kradle {
    kotlinJvmApplication {
        jvm {
            application {
                mainClass("…")
            }
        }
    }
}

Same as:

kradle {
    general {
        bootstrap.enable()
        git.enable()
        projectProperties.enable()
        buildProperties.enable()
    }

    jvm {
        kotlin {
            useCoroutines()
            lint {
                ktlint {
                    rules {
                        disable("no-wildcard-imports")
                    }
                }
            }
            test {
                useKotest()
                useMockk()
            }
        }
        application {
            mainClass("…")
        }

        dependencies.enable()
        vulnerabilityScan.enable()
        lint.enable()
        codeAnalysis.enable()
        developmentMode.enable()

        test {
            prettyPrint(true)
            withIntegrationTests()
            withFunctionalTests()
        }
        codeCoverage.enable()
        benchmark.enable()
        packaging.enable()
        docker {
            withJvmKill()
        }
        documentation.enable()
    }
}

Kotlin/JVM library

kradle {
    kotlinJvmLibrary.activate()
}

Same as:

kradle {
    general {
        bootstrap.enable()
        git.enable()
        projectProperties.enable()
        buildProperties.enable()
    }

    jvm {
        kotlin {
            useCoroutines()
            lint {
                ktlint {
                    rules {
                        disable("no-wildcard-imports")
                    }
                }
            }
            test {
                useKotest()
                useMockk()
            }
        }
        library.enable()
        dependencies.enable()
        vulnerabilityScan.enable()
        lint.enable()
        codeAnalysis.enable()

        test {
            prettyPrint(true)
            withIntegrationTests()
            withFunctionalTests()
        }
        codeCoverage.enable()
        benchmark.enable()
        packaging.enable()
        documentation.enable()
    }
}

Java application

kradle {
    javaApplication {
        jvm {
            application {
                mainClass("…")
            }
        }
    }
}

Same as:

kradle {
    general {
        bootstrap.enable()
        git.enable()
        projectProperties.enable()
        buildProperties.enable()
    }

    jvm {
        java {
            withLombok()
            codeAnalysis {
                spotBugs {
                    useFbContrib()
                    useFindSecBugs()
                }
            }
        }
        application {
            mainClass("…")
        }
        dependencies.enable()
        vulnerabilityScan.enable()
        lint.enable()
        codeAnalysis.enable()
        developmentMode.enable()

        test {
            prettyPrint(true)
            withIntegrationTests()
            withFunctionalTests()
        }
        codeCoverage.enable()
        benchmark.enable()
        packaging.enable()
        docker {
            withJvmKill()
        }
        documentation.enable()
    }
}

Java library

kradle {
    javaLibrary.activate()
}

Same as:

kradle {
    general {
        bootstrap.enable()
        git.enable()
        projectProperties.enable()
        buildProperties.enable()
    }

    jvm {
        java {
            withLombok()
            codeAnalysis {
                spotBugs {
                    useFbContrib()
                    useFindSecBugs()
                }
            }
        }
        library.enable()
        dependencies.enable()
        vulnerabilityScan.enable()
        lint.enable()
        codeAnalysis.enable()

        test {
            prettyPrint(true)
            withIntegrationTests()
            withFunctionalTests()
        }
        codeCoverage.enable()
        benchmark.enable()
        packaging.enable()
        documentation.enable()
    }
}

Configuration DSL reference

This example shows all features enabled with their default configuration.

kradle {

    general {
        bootstrap.enable()
        git.enable()
        buildProfiles {
            active("default")
        }
        projectProperties.enable()
        buildProperties.enable()
        scripts {
            /*
            "<NAME>" {
                description("…")
                dependsOn("…")
                prompt(key = "…", text = "…", default = "…")
                commands("…")
            }
            */
        }
        helm {
            releaseName(project.name)
            // valuesFile("…")
        }
    }

    jvm {
        targetJvm("17")
        kotlin {
            // useCoroutines("1.7.3")
            lint {
                ktlint.enable {
                    version("1.0.1")
                    rules {
                        // disable("…")
                    }
                }
            }
            codeAnalysis {
                detekt.enable {
                    version("1.23.3")
                    configFile("detekt-config.yml")
                }
            }
            test {
                // useKotest("5.7.2")
                // useMockk("1.13.8")
            }
        }
        java {
            previewFeatures(false)
            // withLombok("1.18.30")
            lint {
                checkstyle.enable {
                    version("10.12.4")
                    configFile("checkstyle.xml")
                }
            }
            codeAnalysis {
                pmd.enable {
                    version("6.55.0")
                    ruleSets {
                        bestPractices(false)
                        codeStyle(false)
                        design(false)
                        documentation(false)
                        errorProne(true)
                        multithreading(true)
                        performance(true)
                        security(true)
                    }
                }
                spotBugs.enable {
                    version("4.8.0")
                    useFbContrib("7.6.0")
                    useFindSecBugs("1.12.0")
                }
            }
        }
        application {
            // mainClass("…")
        }
        library.enable() // Conflicts with application

        dependencies {
            // useCaffeine("3.1.8")
            // useGuava("32.1.3-jre")
        }

        vulnerabilityScan.enable()
        lint {
            ignoreFailures(false)
        }
        codeAnalysis {
            ignoreFailures(false)
        }
        developmentMode.enable()

        test {
            junitJupiter.enable {
                version("5.10.0")
            }
            prettyPrint(false)
            showStandardStreams(false)
            withIntegrationTests(false)
            withFunctionalTests(false)
            // withCustomTests("…")
            // useArchUnit("1.1.0")
            // useTestcontainers("1.19.1")
        }

        codeCoverage {
            kover.enable {
                // excludes("…")
            }
            jacoco.configureOnly {
                version("0.8.11")
                // excludes("…")
            }
        }

        benchmark {
            jmh {
                version("1.37")
            }
        }

        packaging {
            uberJar {
                minimize(false)
            }
        }

        docker {
            baseImage("bellsoft/liberica-openjdk-alpine:17")
            imageName(project.name)
            allowInsecureRegistries(false)
            // jvmOptions("…")
            // arguments("…")
            // ports(…)
            // withJvmKill("1.16.0")
            withStartupScript(false)
        }

        documentation.enable()

        logging {
            // withSlf4j("2.0.9")
            // withLog4j("2.21.1")
        }

        frameworks {
            springBoot {
                version("3.1.5")
                // withDevTools("3.1.5")
                // useWeb("3.1.5")
                // useWebFlux("3.1.5")
                // useActuator("3.1.5")
            }
        }
    }
}

How to report bugs

Please open a new issue and if possible provide the output of gradle kradleDump.

Versioning (since 2.0.0)

kradle uses a MAJOR.MINOR.PATCH pattern.

Patch release

Minor release

Major release

License

This project is licensed under the terms of the MIT license.