License
See the LICENSE file for license rights and limitations (MIT).
Overview
This plugin facilitates custom Gradle wrappers construction. For example, we can define common part which is shared through a custom Gradle distribution and have a terse end-project Gradle setup like this (this is a complete build.gradle file):
bootRun {
main = 'com.mycompany.MyApplication'
}
Table of Contents
Problem
Gradle scripts quite often contain duplicate parts, that’s especially true for micro-service architecture where there are many small servers and each of them has its own repository.
One solution to that is putting common parts to a Gradle plugin. However, such extension would be specific to particular company and is unlikely to go to the Gradle plugin repository (to allow shorthand access).
That means that it still would be necessary to have a setup like below in every project:
buildscript {
repositorirs {
maven {
url 'http://artifactory.mycompany.com/internalo'
}
classpath 'com.mycompany:gradle-plugin:3.2.1'
}
}
apply plugin: 'com.mycompany.gradle-plugin'
Solution
Gradle automatically applies init scripts from Gradle wrapper’s init.d directory. That’s why we can do the following:
- Fetch a ‘pure’ Gradle distribution of particular version
- Put our scripts with common logic into it’s init.d directory
- Store that modified Gradle distribution in our repository
- Reference that distribution from the Gradle Wrapper config in our projects
Usage
Configure Custom Distribution
- Create new Gradle project (an empty build.gradle)
- Register the plugin there:
plugins { id 'tech.harmonysoft.oss.custom-gradle-dist-plugin' version '1.11.0' }
- Specify target settings in the
gradleDist {}
block.
mandatory settings:gradleVersion
- base Gradle wrapper versioncustomDistributionVersion
- custom distribution version (project.version
is used by default)customDistributionFileNameMapper
- a property of type CustomDistributionNameMapper which generates resulting custom distribution file name for the given parameters. Note: it’s necessary to specify this property ordistributionNameMapper
property. It’s an error to define the both/none of themgradleVersion
- base gradle distribution version as defined abovecustomDistributionVersion
- custom distribution mixing version as defined abovegradleDistributionType
- gradle distribution type as defined belowdistributionName
- nullable string - it’snull
in case client project produces a single custom Gradle distribution; non-null
distribution name when multiple custom Gradle distributions are produced
we configure it in
build.gradle.kts
as below:import tech.harmonysoft.oss.gradle.dist.config.CustomDistributionNameMapper ... gradleDist { customDistributionFileNameMapper = CustomDistributionNameMapper { gradleVersion: String, customDistributionVersion: String, distributionType: String, distributionName: String? -> "${"$"}{distributionType}-custom-${"$"}{customDistributionVersion}-base-${"$"}{gradleVersion}.zip" } }
this is how it can be configured in
build.gradle
:import tech.harmonysoft.oss.gradle.dist.config.CustomDistributionNameMapper ... gradleDist { customDistributionFileNameMapper = { gradleVersion, customDistributionVersion, distributionType, distributionName -> "${"$"}{distributionType}-custom-${"$"}{customDistributionVersion}-base-${"$"}{gradleVersion}.zip" } as CustomDistributionNameMapper }
customDistributionName
- a unique identifier for the custom Gradle distribution. If this property is defined, then resulting file name isgradle-<gradleVersion>-<customDistributionName>-<customDistributionVersion>-<gradleDistributionType>.zip
, e.g.gradle-8.4-my-company-1.2.3-bin.zip
. I.e. these two setups are the same:gradleDist { ... customDistributionName = "my-company" }
and
gradleDist { ... customDistributionFileNameMapper = { gradleVersion, customDistributionVersion, gradleDistributionType, distributionName -> val prefix = "gradle-$gradleVersion-${config.customDistributionName.get()}-$customDistributionVersion" val suffix = "$gradleDistributionType.zip" distributionName?.let { "$prefix-$it-$suffix" } ?: "$prefix-$suffix" } }
Note: it’s necessary to specify this property or
customDistributionFileNameMapper
property. It’s an error to define the both/none of theminitScriptsSourceDir
- a path to the directory where your initialization scripts are located (see below docs for more details). The path to the directory must be set, and the pointed directory must exist. The default path issrc/main/resources/init.d
.
optional settings:
gradleDistributionType
- allows to specify base Gradle distribution type.bin
andall
are available at the moment,bin
is used by defaultutilityScriptsSourceDir
- a path to the directory where your utility scripts and replacements are located (see below docs for more details). The path to the directory is optional, but the pointed directory must exist. The default path issrc/main/resources/include
.skipContentExpansionFor
- the plugin by default expands content of the files included into custom Gradle distribution by default (see below). That might cause a problem if we want to add some binary file like*.jar
or*.so
. This property holds a list of root paths relative toinit.d
which content shouldn’t be expanded.
Example: consider the following project structure:init.d |__my.gradle | |__bin | |__profiler | |__agent.jar | |__linux-x64 | |__agentti.so
Here we want to expand content for
my.gradle
, but don’t touchbin/profiler/agent.jar
andbin/profiler/linux-x64/agentti.so
. We can configure it as below:gradleDist { ... skipContentExpansionFor = listOf("bin/profiler") }
rootUrlMapper
- a property of type GradleUrlMapper which allows to build an url to the root base Gradle distribution path. This property is convenient in restricted environments where https://service.gradle.org is unavailable. We can deploy target Gradle distribution to a server inside the private network then and use it as a base for our custom Gradle distributions. The function receives the following arguments:version
- target base Gradle distribution version, e.g. 5.1type
- target base Gradle distribution type, e.g.bin
Following implementation is used by default (
build.gradle.kts
):import tech.harmonysoft.oss.gradle.dist.config.GradleUrlMapper ... gradleDist { ... rootUrlMapper = GradleUrlMapper { version: String, type: String -> "https://services.gradle.org/distributions/gradle-${version}-${type}.zip" } }
build.gradle
syntax for the same:import tech.harmonysoft.oss.gradle.dist.config.GradleUrlMapper ... gradleDist { ... rootUrlMapper = { version, type -> "https://services.gradle.org/distributions/gradle-${version}-${type}.zip" } as GradleUrlMapper }
Resulting build.gradle might look like below:
plugins { id 'tech.harmonysoft.oss.custom-gradle-dist-plugin' version '1.11.0' } gradleDist { gradleVersion = '4.10' customDistributionVersion = '1.0' customDistributionName = 'my-project' }
-
Define common setup to be included to the custom Gradle distribution in the project’s
src/main/resources/init.d
directoryNote that the plugin supports simple text processing engine - it’s possible to put utility scripts to the
src/main/resources/include
. Their content is applied to files fromsrc/main/resources/init.d
using$utility-script-name$
syntax.For example, we can have a file
src/main/resources/init.d/setup.gradle
:allprojects { $dependencies$ }
and the following files in the
src/main/resources/include
directory:src/main/resources/include/dependencies.gradle
:dependencies { compile 'com.fasterxml.jackson.core:jackson-core:$jackson-version$' compile 'com.fasterxml.jackson.module:jackson-module-kotlin:$jackson-version$' }
src/main/resources/include/jackson-version.gradle
:2.9.6
When custom Gradle distribution is created, its
init.d
directory hassetup.gradle
file with the following content then:allprojects { compile 'com.fasterxml.jackson.core:jackson-core:2.9.6' compile 'com.fasterxml.jackson.module:jackson-module-kotlin:2.9.6' }
Note that text processing might be nested, i.e. files from
src/main/resources/include
might refer to another files from the same directory through the$file-name$
syntax.Also, we can put any replacements into file
src/main/resources/include/replacements.properties
. Example:include/replacements.properties
spring.plugin.version = 3.1.5 spring.dependency.management.plugin.version = 1.1.3
init.d/mixin.gradle
initscript { dependencies { classpath 'org.springframework.boot:spring-boot-gradle-plugin:$spring.plugin.version$' classpath 'io.spring.gradle:dependency-management-plugin:$spring.dependency.management.plugin.version$' } }
There is an alternative setup where we want to produce more than one Gradle wrapper distribution (e.g.
android
andserver
). In this situation corresponding directories should be created in thesrc/main/resources/init.d
:src |__ main |__ resources |__ init.d |__ android | |__ android-setup.gradle | |__ server |__ server-setup.gradle
Here is what we have after the build:
build |__ gradle-dist |__ gradle-4.10-my-project-1.0-android.zip | |__ gradle-4.10-my-project-1.0-server.zip
-
Build Gradle distribution(s)
./gradlew buildGradleDist
The distribution(s) are located in the
build/gradle-dist
- In addition, if you need, you can configure the properties of the
buildGradleDist
task, such as the path to the directory where downloaded Gradle distributions and built custom Gradle distributions should be located. You can do this as follows:import tech.harmonysoft.oss.gradle.dist.BuildCustomGradleDistributionTask tasks.named('buildGradleDist', BuildCustomGradleDistributionTask) { gradleDownloadDir = project.layout.buildDirectory.dir('own-gradle-download') customDistributionOutputDir = project.layout.buildDirectory.dir('own-gradle-dist') }
Configure Client Project
Just define your custom Gradle wrapper’s location in the gradle/wrapper/gradle-wrapper.properties file:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://mycompany.com/repository/chillout-release/com/mycompany/gradle-dist/gradle-4.10-my-project-1.0.zip
Note About Applying Plugins
It’s quite possible that we would like to apply common plugins in init scripts, e.g. our projects are all java and we want to specify apply plugin: 'java'
in the init script.
Unfortunately, there is a known old bug in Gradle that non-bundled plugins can’t be applied by id in init script.
A solution is to apply them by fully qualified class name.
E.g. given ‘normal’ plugins configuration:
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: "com.jfrog.artifactory"
We should configure them like below in init script:
allprojects {
apply plugin: org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
apply plugin: org.springframework.boot.gradle.plugin.SpringBootPlugin
apply plugin: io.spring.gradle.dependencymanagement.DependencyManagementPlugin
apply plugin: org.jfrog.gradle.plugin.artifactory.ArtifactoryPlugin
}
Examples
Complete working examples can be found here.
Releases
The latest plugin version can be found here.
How to Contribute
Contributors
Feedback
Please use any of the channels below to provide your feedback, it’s really valuable for me: