This project is inspired by The Gradle SuperPOM post by Andres Almiray.
This projects provides two plugins:
-
A Gradle
Projectplugin (id:pl.tlinkowski.gradle.my.superpom) -
A Gradle
Settingsplugin (id:pl.tlinkowski.gradle.my.settings)
Together, those two plugins preconfigure Gradle builds for each of my projects.
gradle.properties:
# Release scopes: [major, minor, patch]
reckon.scope=minor
# Dependencies
mySuperpomVersion=x.y.zsettings.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.
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
Myprefix). - Set up your Bintray and Maven Central accounts.
- Release your own version of the Gradle Settings & SuperPOM plugin.
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(usingmySuperpomVersionproperty ingradle.properties)
-
-
project structure (inspired by Kordamp project structure):
- subprojects in
subprojectsdirectory - build file names changed from
build.gradle.ktsto<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
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:
ideaplugin -
configures: Maven Central repository
-
-
for the root project:
-
applies:
org.kordamp.gradle.projectpluginorg.kordamp.gradle.bintraypluginorg.ajoberstar.grgitpluginorg.ajoberstar.reckonplugincom.github.ben-manes.versionsplugin
-
configures:
-
main project properties using Kordamp DSL (
MyCoreConfigPlugin) -
shared file import tasks (see direct file sharing)
-
SNAPSHOT/FINALrelease stages for reckon (VersionConfigPlugin) -
dependency updates: skipping Release Candidates (
DependencyUpdatesConfigPlugin) -
project Lombok usage (opt-in) (
LombokConfigPlugin)
-
-
-
for subprojects:
-
applies:
-
org.javamodularity.modulepluginplugin (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
testImplementatonscope (ifsuperpom.isTestProjectisfalse— default) -
at
apiscope (ifsuperpom.isTestProjectistrue— opt-in)
-
-
compileTestGroovydependency 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-Nameequal toproject.name(ifmodule-info.javais absent) (ModularityConfigPlugin) -
publishing to JCenter and Maven Central (
MyCentralPublishConfigPlugin)
-
-
This plugin configures a comprehensive release process by:
-
exposing
releaseGradle task (which serves as the root of a complex task chain) -
providing shared
release.batscript (which simply callsgradle cleanfollowed bygradle release -Preckon.stage=final)
The comprehensive release process is configured by
MyComprehensiveReleaseConfigurator
and includes:
- Release validation (requirements: clean repo, pushed
masterbranch,reckon.stage=finalproperty) - 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
minorormajorwhenever we commit any changes that are in such scope
A large part of the build configuration for:
-
this (source) project (defined mostly in the included
buildSrcbuild), and -
target projects (defined in
pl.tlinkowski.gradle.my.superpomplugin 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 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.
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.ideadirectory (subdirectoriescodeStyles,copyright,inspectionProfiles) -
release: files related to releasing, likerelease.batscript and Node.js configuration for gren -
ci: configuration for Continuous Integration environments, i.e..appveyor.ymland.travis.ymlfiles (these files should not be git-ignored in target projects) -
lombok: Lombok configuration, i.e.lombok.configfile
This feature is implemented:
-
in
SuperpomSharedFileExportPlugin, by registering a specialexportSharedFilestask 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 specialimportSharedFilestask for a target project- the task reads the archive as a resource and unzips it in the corresponding location
If superpom.useLombok
is true, this plugin (through LombokConfigPlugin):
-
adds
compileOnlyandannotationProcessordependencies on Project Lombok (likegradle-lombokplugin) -
configures
delombokJavatask, which generates delomboked version of the main Java source code (likegradle-delombokplugin, but in a JPMS-compatible way) -
configures
javadoctask to use the delomboked source code as its source (otherwise, JavaDoc wouldn't reflect code generated by Lombok at all)
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.javais present) -
is valid automatic JPMS module name (only if
module-info.javais absent)
Gradle 5+, JDK 11+.
See my webpage (tlinkowski.pl) or find me on Twitter (@t_linkowski).