A worked example and eight part tutorial on writing, testing and configuring Gradle plugins in Java.
Everything the tutorial teaches is built and tested in this repository.
- Your First Gradle Plugin - implementing
Plugin<Project>, registering a task, and declaring the plugin id. - Your First Plugin Test - driving a real build with TestKit's
GradleRunner. - Declaring Tasks the Right Way - task types, lazy properties, and input/output annotations.
- Making Unit Testable Plugins - separating logic from Gradle so it can be tested in microseconds.
- Making Configurable Plugins - extensions, the project lifecycle, and why providers beat
afterEvaluate. - Reacting to Other Plugins - cooperating with
javaand hooking into lifecycle tasks without forcing anything on anyone. - Wiring Tasks Together - letting providers carry task dependencies instead of maintaining
dependsOnby hand. - Publishing Your Plugin - marker artifacts, the Plugin Portal, and getting a release out without leaking credentials.
Hit an error? TROUBLESHOOTING.md collects the ones people run into most, and what they actually mean.
git clone https://github.com/intisy/gradle-plugin-example.git
cd gradle-plugin-example
./gradlew buildThat compiles the plugin, generates its descriptor, validates the task types, and runs the full test suite. Requires a JDK 17 or newer; Gradle itself comes from the wrapper.
To use the plugin from another build, install it locally:
./gradlew publishToMavenLocalthen, in the consuming project's settings.gradle:
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
}
}and in its build.gradle:
plugins {
id 'io.github.intisy.myplugin' version '1.0.0'
}
myplugin {
fileContent = 'OMGWTFBBQ'
}| Task | Type | What it does |
|---|---|---|
dealwithit |
ad hoc | Prints a greeting from a doLast block |
mytask |
MyTask |
Writes build/myfile.txt using the configured content |
myothertask |
MyTask |
The same type reused for build/otherfile.txt |
mytestabletask |
MyTestableTask |
Writes build/testablefile.txt through the Gradle-free FileCreator |
bundle |
BundleTask |
Concatenates the other tasks' output. Declares no dependsOn; running it still runs its producers |
sourcereport |
SourceReportTask |
Summarises the main source set. Registered only when the java plugin is applied, and hooked into check |
src/main/java/io/github/intisy/
├── MyPlugin.java the plugin entry point, registers everything
├── MyPluginExtension.java the myplugin { } configuration block
├── MyTask.java a configurable, incremental task type
├── MyTestableTask.java the same, delegating to a testable implementation
├── SourceReportTask.java registered only when the java plugin is present
├── BundleTask.java consumes the other tasks' outputs, wired by provider
├── impl/FileCreator.java the actual work, with no dependency on Gradle
├── impl/SourceReporter.java likewise, and deterministic so up to date checks work
└── impl/Bundler.java likewise
src/test/java/io/github/intisy/
├── TestMyPlugin.java ProjectBuilder tests for registration and wiring
├── TestRealBuild.java TestKit tests that run a real build
├── impl/TestFileCreator.java plain unit tests, no Gradle involved
├── impl/TestSourceReporter.java
└── impl/TestBundler.java
testProjects/
├── simpleProject/ fixture applying the plugin with defaults
├── configuredProject/ fixture setting fileContent in a myplugin { } block
└── javaProject/ fixture applying java too, for the sourcereport task
This repository is a GitHub template. After creating your own copy from it, rename everything to your own coordinates in one step:
./bootstrap.sh --group com.acme.tools --id awesome --name awesome-gradle --strip-tutorialThat moves the package trees, rewrites the plugin id, the extension block name, the artifact
coordinates and the docs config, then runs ./gradlew build to prove the result still works. Run it
with --help for the full set of options, and without --strip-tutorial if you want to keep the
tutorial alongside your own code. Windows users should run it from Git Bash or WSL.
A Gradle plugin is a library of code that, when loaded by a build script, adds new functionality to
the build. Gradle ships many built in ones; most users have met java:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}Applying java adds capabilities such as compiling Java sources, running unit tests, and packaging
a JAR. A custom plugin does the same kind of thing for your own conventions.
Rather than repeating custom logic in every build.gradle, you distribute it as a plugin:
- Organisations capture common practice (configuration, packaging, code standards) once and share it across many projects.
- Plugins are unit and integration tested independently, so build system changes can be made with some confidence rather than by hope.
- Plugins are versioned, letting each project control when it takes a build system change.
- Plugins can add genuinely new capability: custom packaging formats, new ways of running tests, and so on.
The result is that project build scripts shrink towards declaring dependencies and little else, while still getting the full set of shared behaviour.
This repository began as a fork of jonathanhood/gradle-plugin-example, whose tutorial was written in Groovy against Gradle 2. The example plugin and all of the tutorials have since been rewritten in Java against modern Gradle and extended with three further parts, covering lazy properties, the configuration cache, and current TestKit. The shape of the lessons is still very much the original author's.