Gradle SuperPOM of Tomasz Linkowski
This project is inspired by The Gradle SuperPOM post by Andres Almiray.
This projects provides two plugins:
-
A Gradle
Project
plugin (id:pl.tlinkowski.gradle.my.superpom
) -
A Gradle
Settings
plugin (id:pl.tlinkowski.gradle.my.settings
)
Together, those two plugins preconfigure Gradle builds for each of my projects.
Usage
by Tomasz Linkowski
gradle.properties
:
# Release scopes: [major, minor, patch]
reckon.scope=minor
# Dependencies
mySuperpomVersion=x.y.z
settings.gradle.kts
:
buildscript {
repositories {
mavenCentral()
}
dependencies {
val mySuperpomVersion: String by settings
classpath(group = "pl.tlinkowski.gradle.my", name = "pl.tlinkowski.gradle.my.settings", version = mySuperpomVersion)
}
}
apply(plugin = "pl.tlinkowski.gradle.my.settings")
build.gradle.kts
:
plugins {
id("pl.tlinkowski.gradle.my.superpom")
}
For a complete usage example, see sample-project.
by others
If you like what this plugin does, you can:
- Fork this project.
- Change data related to Tomasz Linkowski to match your person / organization (especially classes with
My
prefix). - Set up your Bintray and Maven Central accounts.
- Release your own version of the Gradle Settings & SuperPOM plugin.
Features
Settings Plugin (id: pl.tlinkowski.gradle.my.settings
)
Configures:
-
plugin management:
-
Maven Central repository for
pl.tlinkowski.gradle.my.superpom
(this plugin is not deployed to Gradle Plugin Portal as it's not a general-use plugin) -
automatic version resolution for
pl.tlinkowski.gradle.my.superpom
(usingmySuperpomVersion
property ingradle.properties
)
-
-
project structure (inspired by Kordamp project structure):
- subprojects in
subprojects
directory - build file names changed from
build.gradle.kts
to<subproject-name>.gradle.kts
- build files required for all subprojects
- subprojects optionally grouped under subdirectories (e.g.
subprojects/sample/pl.tlinkowski.xyz.sample
)
- subprojects in
Project Plugin (id: pl.tlinkowski.gradle.my.superpom
)
Project Preconfiguration
This is the basic feature described by Andres Almiray in his post.
This SuperPOM plugin can be applied to the root project only, and it does the following:
-
for all projects:
-
applies:
idea
plugin -
configures: Maven Central repository
-
-
for the root project:
-
applies:
org.kordamp.gradle.project
pluginorg.kordamp.gradle.bintray
pluginorg.ajoberstar.grgit
pluginorg.ajoberstar.reckon
plugincom.github.ben-manes.versions
plugin
-
configures:
-
main project properties using Kordamp DSL (
MyCoreConfigPlugin
) -
shared file import tasks (see direct file sharing)
-
SNAPSHOT
/FINAL
release stages for reckon (VersionConfigPlugin
) -
dependency updates: skipping Release Candidates (
DependencyUpdatesConfigPlugin
) -
project Lombok usage (opt-in) (
LombokConfigPlugin
)
-
-
-
for subprojects:
-
applies:
-
org.javamodularity.moduleplugin
plugin (for JPMS support) -
kotlin("jvm")
plugin (for test code: custom helpers)
-
-
configures:
-
logging of test events (
TestConfigPlugin
) -
test dependencies on Kotlin, Groovy, and Spock (
TestConfigPlugin
)-
at
testImplementaton
scope (ifsuperpom.isTestProject
isfalse
— default) -
at
api
scope (ifsuperpom.isTestProject
istrue
— opt-in)
-
-
compileTestGroovy
dependency oncompileTestKotlin
(so that Spock can access Kotlin helpers) (TestConfigPlugin
) -
running tests on classpath (necessary as Groovy isn't JPMS-compatible) (
ModularityConfigPlugin
) -
minimum line code coverage = 95% (JaCoCo) (
JacocoConfigPlugin
) -
project name and module name validation (see Naming Convention) (
NamingConventionEnforcementPlugin
) -
Automatic-Module-Name
equal toproject.name
(ifmodule-info.java
is absent) (ModularityConfigPlugin
) -
publishing to JCenter and Maven Central (
MyCentralPublishConfigPlugin
)
-
-
Comprehensive Release Process
This plugin configures a comprehensive release process by:
-
exposing
release
Gradle task (which serves as the root of a complex task chain) -
providing shared
release.bat
script (which simply callsgradle clean
followed bygradle release -Preckon.stage=final
)
The comprehensive release process is configured by MyComprehensiveReleaseConfigurator
and includes:
- Release validation (requirements: clean repo, pushed
master
branch,reckon.stage=final
property) - Full clean build (to make 100% sure we can release)
- Confirmations to make sure the release is going fine
- Changelog generation (by gren, requires Node.js)
- Tagging the release in Git
- Publishing to GitHub (by gren, requires Node.js)
- Publishing to central repos (JCenter & Maven Central)
- Post-release reset of the release scope for reckon in
gradle.properties
This is how a Gradle build log of such a release process looks:
> Task :validateReleasePossible // 1
> Task :<subproject-1>:[...] // 2
> Task :<subproject-1>:build // 2
> Task :<subproject-2>:[...] // 2
> Task :<subproject-2>:build // 2
> Task :confirmReleaseProcessLaunch // 3
=== Do you want to begin the release process for version 0.1.0 of 'sample-project'? [y/N] ===
y
> Task :addTemporaryVersionTag // 4 (required by gren)
> Task :generateChangelog // 4 (gren)
> Task :removeTemporaryVersionTag // 4 (no longer needed)
> Task :confirmChangelogPush // 3
=== Do you want to push the updated CHANGELOG.md and continue with the release process? [y/N] ===
y
> Task :pushUpdatedChangelog // 4
> Task :addFinalVersionTag // 5
> Task :confirmFinalPublication // 3
=== Are you SURE you want to publish the code at 0.1.0 tag to GitHub, JCenter & MavenCentral? [y/N] ===
y
> Task :releaseToGitHub // 6 (gren)
> Task :<subproject-1>:[...] // 7
> Task :<subproject-1>:publishMainPublicationToMavenLocal // 7
> Task :<subproject-2>:[...] // 7
> Task :<subproject-2>:publishMainPublicationToMavenLocal // 7
> Task :injectReleasePasswords // 7
> Task :<subproject-1>:[...] // 7
> Task :<subproject-1>:bintrayUpload // 7
> Task :<subproject-2>:[...] // 7
> Task :<subproject-2>:bintrayUpload // 7
> Task :bintrayPublish // 7
> Task :releaseToCentralRepos // 7
> Task :release
> Task :resetScopeInGradleProperties // 8
> Task :pushUpdatedGradleProperties // 8
Note the injectReleasePasswords
task, which obtains the following passwords for performing a release:
bintrayApiKey
: from Gradle properties (i.e.~/.gradle/gradle.properties
),gnupgPassphrase
,sonatypePassword
: by requesting them in a Swing dialog (not suitable for CI)
Also, note that thanks to reckon plugin, we don't need to do the classic "pre-release version bumps". Instead, we:
- automatically reset the version scope after a release to
patch
(= "post-release version bump") - manually change the scope to
minor
ormajor
whenever we commit any changes that are in such scope
Gradle Configuration Sharing
A large part of the build configuration for:
-
this (source) project (defined mostly in the included
buildSrc
build), and -
target projects (defined in
pl.tlinkowski.gradle.my.superpom
plugin project)
is shared as pl.tlinkowski.gradle.my.superpom.shared
package (see MyCompleteSharedConfigPlugin
).
Thanks to this, we don't have to:
-
duplicate large portions of configuration between the source and target projects, nor
-
apply the previous version of this plugin to itself to avoid the duplication mentioned above (as Andres Almiray suggests in his post)
- such approach would be problematic for direct file sharing
This is achieved by synchronizing the contents of the SuperPOM plugin's shared
package with a corresponding shared
package in buildSrc
(see buildSrc/build.gradle.kts
for details).
Gradle Property Sharing
Gradle properties at gradle/shared-gradle.properties
are shared by SuperpomSharedFileExportPlugin
(a part of direct file sharing mechanism). Then, these properties are imported by:
-
by
shared-gradle-properties.gradle.kts
, forbuildSrc
, rootsettings.gradle.kts
, and rootbuild.gradle.kts
-
by
SuperpomSharedGradlePropertyImportPlugin
, for all target projects
If a shared property to be imported already exists, it's ignored with a warning.
Direct File Sharing
Selected files in this project can be directly exported to projects that apply this SuperPOM plugin. It can be viewed as a "sync" operation between this (source) project and all target projects.
The files to be shared are specified in SuperpomFileSharing
(usually, it's a good idea to git-ignore them in target projects). Currently, the following files are shared directly:
-
idea
: parts of IntelliJ configuration from.idea
directory (subdirectoriescodeStyles
,copyright
,inspectionProfiles
) -
release
: files related to releasing, likerelease.bat
script and Node.js configuration for gren -
ci
: configuration for Continuous Integration environments, i.e..appveyor.yml
and.travis.yml
files (these files should not be git-ignored in target projects) -
lombok
: Lombok configuration, i.e.lombok.config
file
This feature is implemented:
-
in
SuperpomSharedFileExportPlugin
, by registering a specialexportSharedFiles
task for this (source) project- the task zips files to be exported and places the resulting archive in the resources of the SuperPOM plugin
-
in
SuperpomSharedFileImportPlugin
, by registering a specialimportSharedFiles
task for a target project- the task reads the archive as a resource and unzips it in the corresponding location
Lombok
If superpom.useLombok
is true
, this plugin (through LombokConfigPlugin
):
-
adds
compileOnly
andannotationProcessor
dependencies on Project Lombok (likegradle-lombok
plugin) -
configures
delombokJava
task, which generates delomboked version of the main Java source code (likegradle-delombok
plugin, but in a JPMS-compatible way) -
configures
javadoc
task to use the delomboked source code as its source (otherwise, JavaDoc wouldn't reflect code generated by Lombok at all)
Naming Convention
This project applies a naming convention for Maven & JPMS by Christian Stein. In short:
Gradle project name = JPMS module name
The SuperPOM plugin enforces this convention by ensuring that the Gradle project name (i.e. Maven artifactId
):
-
starts with Maven
groupId
-
is a prefix of every package in the project
-
equals JPMS module name (only if
module-info.java
is present) -
is valid automatic JPMS module name (only if
module-info.java
is absent)
Requirements
Gradle 5+, JDK 11+.
About the Author
See my webpage (tlinkowski.pl) or find me on Twitter (@t_linkowski).