gradle init for Spring Boot
Production-ready Gradle plugin inspired by DPE University courses.

Why yet another Gradle plugin
Recently, I wanted to deepen my understanding of Build Scans, Develocity, and their practical applications, so I started going through the courses on our DPE University platform. In my daily work, I interact with Develocity from the operational and production side — maintaining open-source instances among other things — so I wanted to better understand the product, especially from the end-user perspective, to support my daily engineering work.
To systematize what I was learning, I decided to build a project that aligns with the best practices discussed in these courses. At the same time, I was working on a Spring Boot microservice generated via Spring Initializr and noticed there was no Gradle plugin that would automate this process in a way that is build cache-friendly and CI/CD-friendly.
That’s how the gradle-springinitializr-plugin was born—a production-ready tool that simplifies bootstrapping Spring Boot projects directly from Gradle. This post covers how it was built, how it works, and how you can use it to speed up starting new projects within your team.
The plugin is open-source and available on the Gradle - Plugins. You can install and start using it right away.
Create the build.gradle
file and add the following:
|
|
Create the settings.gradle
file and add this configuration:
|
|
Or add it to your existing project.
This way, you can generate new Spring Boot projects with Gradle while reading this post, accelerating your onboarding and daily work.
👉 GitHub
I treat this plugin as a reference: how to build a production-ready plugin in Groovy consistent with Gradle best practices. If you build plugins yourself, you can use it as a point of reference.
Who is this plugin for
Platform Engineers
gradle-springinitializr-plugin can become a part of your platform to accelerate microservice scaffolding across your organization. With it, you can:
✅ Quickly generate Spring Boot microservices (Groovy/Java/Kotlin, Gradle/Maven) from CLI, pipelines, or automated GitOps processes.
✅ Always generate projects with the latest Spring Boot, Java, and dependency versions, eliminating the need to manually maintain templates.
✅ Provide a build cache-aware, CI-friendly workflow from day one of your projects.
✅ Standardize project structure, metadata (groupId, artifactId, packageName, description), and the microservice creation process.
If you are building an engineering platform for JVM-based developer teams, this plugin can become a lightweight, configurable, and automatable pipeline element for “just-in-time” scaffolding of Spring Boot projects.
Software Engineers
gradle-springinitializr-plugin can become your go-to tool for quickly starting Spring Boot projects without leaving the Gradle ecosystem. Instead of clicking through the Spring Initializr web UI or writing curl commands, you can generate a fully customizable project with a single Gradle command. It saves time, ensures consistent configurations, and integrates immediately into your build cache, CI/CD, and team conventions.
The Curious and Mindful
gradle-springinitializr-plugin lets you see how Spring Boot project generation from Gradle works in under a minute, without clicking around https://start.spring.io or writing curl commands. Instead of manually downloading ZIP files, you can instantly start a project locally and explore Spring Boot in a repeatable, clean, and user-friendly way.
How it works
After installing the gradle-springinitializr-plugin, you can generate Spring Boot projects directly from Gradle without leaving your terminal.
The plugin provides a single task initSpringBootProject
, which downloads a project from Spring Initializr and extracts it. Instead of clicking in the web interface, simply run:
|
|
You will get:
|
|
By default the project will be downloaded and extracted into build/generated-project/demo
, ready to open and run.
The plugin also allows you to set any parameter available in Spring Initializr:
|
|
This way you generate clean, consistent projects ready for your CI/CD workflow and aligned with team conventions – without manual steps.
The plugin also supports an experimental interactive mode inspired by gradle init
:
This allows you to enter all project parameters without remembering command line flags – the plugin guides you step by step through choosing Spring Boot version, language, project type, and dependencies. It’s particularly useful for new team members or when you want to quickly prepare a project aligned with your organization’s standards.
To use interactive mode, follow the instructions in the FAQ.
How it’s built
As mentioned earlier, besides using knowledge from DPE University, I wanted this plugin to be a reference implementation for building further plugins. My goal was to create something solid, nearly maintenance-free, and as timeless as possible.
plugin.properties
The heart of the plugin is the plugin.properties
file, which contains default parameter values. This supports the principle of separating code and configuration - changes to values do not require code modification or recompilation, just file replacement.
Properties are loaded once at plugin startup and available through static constants:
|
|
This approach simplifies both production and test configuration – giving full control over the environment without changes to plugin logic.
For plugin.properties
to be visible in functional tests as a resource, add to build.gradle
:
|
|
This way functional tests use exactly the same configuration as the production plugin.
convention
: clean plugin configuration
Options such as initializrUrl
, metadataEndpoint
and extract
are designed as global plugin configuration – they concern the source of data (where to fetch the project from), not the project content itself (what it should include). This keeps separation between infrastructural configuration and actual generation logic.
Default values are set using convention(...)
in Gradle - which means:
- the user does not have to set anything to use the plugin in default mode,
- but can override any value in build.gradle, for example to use their own Spring Initializr instance or disable ZIP extraction:
|
|
This approach provides:
- clean builds – no mixing of configuration with invocation parameters.
- predictability – the plugin always works on explicitly defined inputs.
- flexibility – the user can point to a custom endpoint, e.g. in CI or a mirror.
See official Gradle documentation on convention(...)
for more details on setting clear, overridable defaults in your plugins and tasks.
Extracting available options from Metadata API
The plugin fetches supported versions of Spring Boot, project types, and languages available after downloading from Spring Initializr. This is done via the Metadata endpoint. Thanks to this, there is no need to constantly update the plugin for supported parameters.
While adding supported project types and languages I encountered an interesting issue with configuration cache in Gradle. More on this below.
At first I ran into problems testing the plugin with the --configuration-cache
flag. Initially I thought the issue was reading plugin.properties
, but the real cause was using the project.*
API in the class extending DefaultTask
:
|
|
This breaks configuration cache, because Gradle requires all inputs to be explicitly declared using Property<T>
, Provider<T>
or DirectoryProperty
. Then during execution phase you cannot refer to mutable project state.
I solved this by:
- Moving all logic of fetching supported project types and languages into the download() method.
- Ensuring that
@TaskAction
operates only on properties declared asProperty<T>
orProvider<T>
, evaluated at execution time.
This change not only enabled configuration cache compatibility, but also led to a cleaner, more idiomatic implementation aligned with Gradle practices.
See official Gradle documentation on configuration cache for all details and limitations.
-P
parameters and validation mechanism
The plugin accepts all parameters available at https://start.spring.io — the same ones you can set in the web UI of Spring Initializr.
After users set parameters or default values are assigned, validation is performed inside the initSpringBootProject
task. At the beginning, the task fetches the list of supported Spring Boot versions, project types, and languages from the Metadata endpoint. If a user provides an invalid parameter value that is not supported, the task will fail. For example:
|
|
Will result in the message:
|
|
Incremental build and build cache
Based on the Gradle Build Caching path at DPE University and the knowledge gained there, the plugin was given support for incremental build and Build Cache. Thanks to this, subsequent executions are instant, and results can be reused.
The initSpringBootProject
task declares both inputs (@Input
) and outputs (@OutputDirectory
— in this case outputDir
, tied to a DirectoryProperty
). This is aligned with the definition of a correctly configured task in Gradle, described here.
Thanks to this, Gradle has full control over tracking the state of inputs and outputs, which enables:
- Skipping the task (
UP-TO-DATE
) if the outputs have not changed — incremental build. - Restoring results (
FROM-CACHE
) if outputs exist in the local cache — build cache.
Below is an example of how the task behaves in both cases.
Incremental build
On the first run, the project is downloaded from Spring Initializr:
|
|
You will see:
|
|
On subsequent runs with the same -PoutputDir
parameter:
|
|
Since the outputs haven’t changed, you’ll see:
|
|
Which confirms that incremental build was used, marked with the UP-TO-DATE
label.
Build cache
If the file /opt/my-projects/my-spring-boot-app/starter.zip
is deleted and the task is run again:
|
|
Since the outputs were previously added to the local cache, the response will show:
|
|
This confirms that the local build cache was used, indicated by the FROM-CACHE
label.
While going through the Gradle Build Caching path at DPE University, I became curious about how exactly the local build cache works and what actually goes inside it. I decided to break it down.
After running the initSpringBootProject
task, the build cache key 75b4d5a65d989aa6453584fe39baeab5
stands out. That’s an archive located in $GRADLE_USER_HOME/caches/build-cache-1
. When unpacked, you can see, among others, a METADATA
file that contains:
|
|
In the tree-outputDir/
folder there is the archived starter.zip
file and the project folder — exactly what was previously downloaded from Spring Initializr and saved as the task’s outputs.
For more on incremental build and Build Cache, see the official documentation:
👉 Build Cache.
Interactive mode
To preserve compatibility with incremental build and build cache, upToDateWhen
and cacheIf
were used accordingly.
Production readiness
When building this plugin I wanted it to be ready for production use from day one. That means not just “it works on my machine”, but it is tested, validated, analyzable, and integrated with CI/CD.
Unit tests
Written in Spock. They cover key functionalities such as parameter validation, building queries to Spring Initializr, and extraction. In line with the testing pyramid, these are the most numerous tests in the project.
Functional tests
Also written in Spock, this time using Gradle TestKit. In addition, I test JSON responses from Spring Initializr with WireMock, following the official documentation recommended by the Spring Initializr team.
At the beginning, while working on functional tests, I ran into a case described in the TestKit documentation in the section Controlling the build environment.
Even though the initSpringBootProject
task executed successfully and in the logs I could see:
|
|
The tests did not pass, and I was getting the following message in the logs:
|
|
The issue came from the fact that Gradle TestKit always runs the build in an isolated working directory inside java.io.tmpdir
(e.g. /private/var/folders/.../build/
), and not in the directory where I expected the files.
However, in the assertion (generatedProjectDir.absolutePath
) it was looking for files in Spock’s temporary directory (@TempDir
), which is a different location than the working directory used by TestKit.
I solved it by:
- Setting
-PoutputDir=${generatedProjectDir.absolutePath}
, which forced the plugin to save files in the place expected by the test.
Static code analysis
Every tool released for use should include a mechanism for static code analysis. In this case, the CodeNarc plugin was used, with a necessary set of rules useful for tools like Gradle plugins.
Test coverage
As mentioned earlier, gradle-springinitializr-plugin is covered by both unit and functional tests. Coverage is measured using the JaCoCo plugin. It includes both types of tests and must exceed 80%.
Continuous integration
For continuous integration I used GitHub Actions. On every PR, it verifies whether the commit follows Conventional Commits. Then it runs gradle build
, which checks the following:
- Code compilation
- Unit and functional tests
- Static code analysis
- Test coverage
Build Scan
At the end of the CI process, a Build Scan is published to https://scans.gradle.com. An example Build Scan from an execution can be found here.
Compatibility tests
While building the plugin, it was important to ensure compatibility with multiple versions of Gradle, as well as both Kotlin DSL and Groovy DSL. That’s why on every PR, in addition to the previously mentioned steps, compatibility tests are run. They execute the initSpringBootProject
task on different versions of Gradle using the following directories:
examples/simple-groovy
examples/simple-kotlin
All of this is possible thanks to the matrix strategy in GitHub Actions. More about it can be found here.
Continuous delivery
After the CI process described above, when a commit lands in main
, GitHub Actions triggers a workflow that uses semantic-release to manage changes in CHANGELOG.md
, create a new tag with a release in the repository, and publish the plugin to Gradle - Plugins.
Summary
While building gradle-springinitializr-plugin, I not only enjoyed recreating gradle init
for Spring Boot, going through DPE University courses, exploring the Spring Initializr API, Gradle best practices, Spock and Groovy. I am also fully satisfied with the final result. This is not just another sample plugin. It is a tool ready to use locally and as part of a platform — built consciously, solidly, and following the best patterns.
For Platform Engineers it’s a ready-to-use asset for the platform, for Software Engineers – a quick start in daily work, and for explorers – a practical reference for building Gradle plugins.
Interested in contributing to the plugin?
👉 See how you can get involved by checking the CONTRIBUTING file.
Have an idea for a new feature or found a bug?
👉 Submit your idea or report an issue here.