The Gradle Enterprise Maven extension improves your development workflow and your productivity, when developing and maintaining Apache Maven™ builds. The extension enables build scan insights and build cache acceleration.
Getting set up with the Gradle Enterprise Maven Extension
You apply the Gradle Enterprise Maven extension to your build by adding the following configuration block to a new or existing .mvn/extensions.xml
file in your Maven project. The extension will be downloaded automatically from Maven Central once you run your build.
<extensions>
<extension>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
</extension>
</extensions>
You can also add the extension jar into the lib/ext
folder of your Maven installation. This is useful if you are packaging a custom Maven installation for your organization and you want Gradle Enterprise to be available to all your projects out of the box.
The extension is configured through one or more gradle-enterprise.xml
files and the pluginManagement
sections of your pom.xml
files. The configuration options will be introduced over the coming sections. For the full reference of the extension’s configuration, see the Configuration reference.
Since version 1.2, the Gradle Enterprise Maven extension captures an identifier used to uniquely represent a given workspace.
|
Using build scans
Build scans are a record of what happened during a build, captured and visualized by Gradle Enterprise.
Build scans are an important tool for developing and maintaining Maven builds. They provide insights into exactly what your builds are doing, helping you identify problems with the build environment, performance, and more. They can also help you understand and improve the build more generally, and make collaborating with others easier.
Gradle Enterprise is a commercial product for companies that can be hosted on their own systems and ships with a build scan server and a build cache backend implementation. scans.gradle.com is a build scan server available for free, hosted by Gradle Inc.
There are two aspects to working with build scans:
-
Data collection
-
Publishing
Enabling publication of build scans
Enabling publication of build scans depends on whether you are publishing to a Gradle Enterprise instance or scans.gradle.com. In the case of Gradle Enterprise, you need to specify the server’s location. In the case of scans.gradle.com, you need to accept the terms of service.
Set the location of your Gradle Enterprise instance
When you publish build scans to a Gradle Enterprise instance, you must configure the location of the Gradle Enterprise server.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.setServer("https://gradle-enterprise.mycompany.com");
$ mvn package -Dgradle.enterprise.url=https://gradle-enterprise.mycompany.com
<gradleEnterprise>
<server>
<url>https://gradle-enterprise.mycompany.com</url>
</server>
</gradleEnterprise>
The precise URL you need depends on the hostname that your Gradle Enterprise instance has been configured with. If in doubt, be sure to ask whomever manages that instance.
You may encounter a warning about an untrusted certificate when connecting to Gradle Enterprise over HTTPS. The ideal solution is for someone to add a valid SSL certificate to the Gradle Enterprise instance, but we recognise that you may not be able to do that. In this case, set the allowUntrusted
option to true
:
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.setAllowUntrusted(true);
<gradleEnterprise>
<server>
<allowUntrusted>true</allowUntrusted>
</server>
</gradleEnterprise>
This is a convenient workaround, but you shouldn’t use it as a long-term solution.
Accept the scans.gradle.com terms of service
In order to publish to scans.gradle.com, you need to accept the terms of service.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.setTermsOfServiceUrl("https://gradle.com/terms-of-service");
buildScan.setTermsOfServiceAgree("true");
<gradleEnterprise>
<buildScan>
<termsOfService>
<url>https://gradle.com/terms-of-service</url>
<accept>true</accept>
</termsOfService>
</buildScan>
</gradleEnterprise>
Be sure to check the terms of service at the URL shown in the above fragment.
Once you have accepted the terms of service, you can start publishing build scans to scans.gradle.com.
If you don’t accept the terms of service, as explained above, you will be prompted to agree to the terms of service on the command line, before any attempt of publishing a build scan. This can be useful if you share your |
Integrating your CI tool
The Gradle plugin for Jenkins prominently displays links to the build scan for any Maven builds that produce build scans. This makes viewing the build scan of CI builds much easier.

Controlling when build scans are published
Once you’ve gone through the initial setup of the previous section, you are ready to start publishing build scans. But when should you publish them? Every time you run a build? Only when the build fails? It’s up to you. The Gradle Enterprise Maven extension has several options that allow you to use whatever approach works best.
Publishing every build run
This is the default. There are many advantages to publishing build scans regularly, such as being able to track the behavior and performance of a build over time. It makes no sense relying on ad-hoc publishing of scans in such a situation as it’s easy to forget on the command line. Should you decide to explicitly enforce this default option, you can do this as follows:
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.publishAlways();
<gradleEnterprise>
<buildScan>
<publish>ALWAYS</publish>
</buildScan>
</gradleEnterprise>
This approach means that you get a build scan for every successful and failed build that runs, including from your continuous integration infrastructure and your developers.
Publishing on demand
We imagine that when you first start experimenting with build scans, you won’t want to publish them all the time until you become familiar with the implications. Even then, you may have good reason not to go all-in and automate the process. That’s where one-off build scans come in.
If you only want to publish build scans when explicitly requested, use the following option:
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.publishOnDemand();
-Dscan
For instance:
$ mvn clean verify -Dscan
<gradleEnterprise>
<buildScan>
<publish>ON_DEMAND</publish>
</buildScan>
</gradleEnterprise>
You can then publish a build scan by passing the -Dscan
system property to Maven.
$ mvn clean verify -Dscan
Publishing based on criteria
Many of you will want a bit more control over exactly when build scans are published without resorting to using -Dscan
each time. Perhaps you only want to publish build scans when the build fails, or if the build is running on your continuous integration infrastructure. Such scenarios are covered by the options in the following table.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.publishAlwaysIf(true); // Publish a build scan if the given condition is true
, regardless of whether the build succeeds or fails
buildScan.publishOnFailure(); // Publish a build scan only when the build fails
buildScan.publishOnFailureIf(true); // Publish a build scan only if the condition is true
and the build fails
<gradleEnterprise>
<buildScan>
<publish>ON_FAILURE</publish>
</buildScan>
</gradleEnterprise>
Conditional publication is not available via XML.
Giving a more concrete example, let’s say you only want to publish build scans from your CI system, which is identified by having a CI
environment variable. This configuration will do the trick:
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.publishAlwaysIf(System.getenv("CI") != null);
-Dgradle.user.config
For instance:
$ mvn clean verify -Dgradle.user.config="location of a gradle-entreprise.xml on your CI agents, configured to always publish"
If you want to publish build scans only from your CI system,
you have to use a specific gradle-enterprise.xml
file
located at <maven-home>/conf/gradle-enterprise.xml
in your CI agents, containing:
<gradleEnterprise>
<buildScan>
<publish>ALWAYS</publish>
</buildScan>
</gradleEnterprise>
Authenticating with Gradle Enterprise
(Maven extension 1.3+, Gradle Enterprise 2019.4+)
Gradle Enterprise installations may be configured to require build scan publishing to be authenticated. Additionally, installations may be configured to only allow certain users to publish build scans.
Gradle Enterprise access keys should be treated with the same secrecy as passwords. They are used to authorize access to Gradle Enterprise from a build. |
Automated access key provisioning
The easiest way to configure a build environment to authenticate with Gradle Enterprise is to use the the following goal:
$ mvn com.gradle:gradle-enterprise-maven-extension:1.3.2:provision-access-key
When executed, it opens your web browser and asks to confirm provisioning of a new access key. You will be asked to log in to Gradle Enterprise in your browser first if you are not already logged in.
When confirmed, a new access key will be generated and stored in the .gradle-enterprise/keys.properties
file within the Maven user home directory (~/.m2
by default).
Any existing access key for the same server will be replaced in the file, but will not be revoked at the server for use elsewhere. To revoke old access keys, log in to Gradle Enterprise and access “My settings” via the user menu at the top right of the page.
If your browser cannot be opened automatically at the correct page, you will be asked to manually open a link provided in the build console. |
Manual access key configuration
Access keys can also be configured manually for an environment, when automated provisioning is not suitable.
Creating access keys
To create a new access key, log in to Gradle Enterprise and access “My settings” via the user menu at the top right of the page. From there, use the “Access keys” section to generate an access key.
The access key value should then be copied and configured in your build environment via file or via environment variable.
Via file
Gradle Enterprise access keys are stored inside the Maven user home directory (~/.m2
by default), at .gradle-enterprise/keys.properties
, in a Java properties file. The property name refers to the host name of the server, and the value is the access key.
gradle-enterprise.mycompany.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq
The file may contain multiple entries. The first entry for a given host value will be used.
Via environment variable
The access key may also be specified via the GRADLE_ENTERPRISE_ACCESS_KEY
environment variable. This is typically more suitable for CI build environments.
The environment variable value format is «server host name»=«access key»
.
$ export GRADLE_ENTERPRISE_ACCESS_KEY=gradle-enterprise.mycompany.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq $ mvn package
The server host name is specified in order to prevent the access key being transmitted to a different server than intended.
Capturing goal input files
(Maven extension v1.1+)
Build scans capture hashes of goal inputs, to enable identifying changes to inputs when comparing builds, among other features. By default, an overall hash value for each goal input property is captured. This enables identifying which properties changed for a goal execution (e.g. the source or the classpath for Java compilation), but not which individual files changed. In order to identify file changes, the paths and content hashes of each individual input file must be captured, which can be enabled.
When to enable
Capturing goal input files increases the amount of data transmitted to the build scan server at the end of the build. If the network connection to the build scan server is poor, it may increase the time required to transmit. Additionally, it may also increase the data storage requirements for the build scan server.
This data is currently only used for build comparison, which is only available in Gradle Enterprise and is not available with scans.gradle.com. If you are using scans.gradle.com, it is not recommended that you enable capture of goal input files.
If you are using Gradle Enterprise and utilising its build cache to accelerate your builds, it is strongly recommended to enable capture of goal input files as identifying which files have changed between builds with build comparison is extremely effective for diagnosing unexpected build cache misses.
How to enable
Goal input files capture can be enabled programmatically, via a system property, or via the gradle-enterprise.xml
configuration file.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.setCaptureGoalInputFiles(true);
-Dgradle.scan.captureGoalInputFiles
For instance:
$ mvn clean verify -Dgradle.scan.captureGoalInputFiles=true
<gradleEnterprise>
<buildScan>
<captureGoalInputFiles>true</captureGoalInputFiles>
</buildScan>
</gradleEnterprise>
Extending build scans
You can easily include extra custom information in your build scans in the form of tags, links and values. This is a very powerful mechanism for capturing and sharing information that is important to your build and development process.
This information can be anything you like. You can tag all builds run by your continuous integration tool with a CI
tag. You can capture the name of the environment that the build published to as a value. You can link to the source revision for the build in an online tool such as GitHub. The possibilities are endless.
You can see how the custom data appears in figure 2:

Gradle Enterprise allows listing and searching across all of the build scans in the system. You can find and filter build scans by tags and custom values, in addition to project name, outcome and other properties. In figure 3, for example, we are filtering for all build scans that have the tag "local" and a git branch name of "master":

Adding tags
Tags are typically used to indicate the type or class of a build, or a key characteristic. They are prominent in the user interface and quickly inform a user about the nature of a build. A build can have zero or more tags.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.tag("my tag");
-Dscan.tag.<tag>
For instance:
$ mvn package -Dscan.tag.CI
<gradleEnterprise>
<buildScan>
<tags>
<tag>my tag</tag>
</tags>
</buildScan>
</gradleEnterprise>
pom.xml
<project>
...
<plugins>
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<buildScan>
<tags>
<tag>my tag</tag>
</tags>
</buildScan>
</gradleEnterprise>
</configuration>
</plugin>
</plugins>
</project>
Prefer the programmatic access.
If specified in parent POM, tags will be applied to all projects inheriting from it.
Note that the order in which you declare the tags doesn’t affect the build scan view. They are displayed in alphabetical order, with any all-caps labels displayed before the rest.
You can see the effect of a custom tag in figure 2.
The Gradle Enterprise Maven extension imposes limits on captured tags:
|
Adding links
Builds rarely live in isolation. Where does the project source live? Is there online documentation for the project? Where can you find the project’s issue tracker? If these exist and have a URL, you can add them to the build scan.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.link("my link", "http://my-site.com");
-Dscan.link.<name>=<URL>
For instance:
$ mvn package -Dscan.link.VCS=https://github.com/myorg/my-super-project/tree/my-new-feature
<gradleEnterprise>
<buildScan>
<links>
<link>
<name>my link</name>
<url>http://my-site.com</url>
</link>
</links>
</buildScan>
</gradleEnterprise>
pom.xml
<project>
...
<plugins>
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<buildScan>
<links>
<link>
<name>my link</name>
<url>http://my-site.com</url>
</link>
</links>
</buildScan>
</gradleEnterprise>
</configuration>
</plugin>
</plugins>
</project>
Prefer the programmatic access.
If specified in parent POM, links will be applied to all projects inheriting from it.
Links can also be added in your project POM.
The <name> is simply a string identifier that you choose and that means something to you.
You can see the effect of a custom link in figure 2, which shows how a label CVS becomes a hyperlink that anyone viewing the build scan can follow.
The Gradle Enterprise Maven extension imposes limits on captured links:
|
Adding custom values
Some information just isn’t useful without context. What does "1G" mean? You might guess that it represents 1 gigabyte, but of what? It’s only when you attach the label "Max heap size for build" that it makes sense. The same applies to git commit IDs, for example, which could be interpreted as some other checksum without a suitable label.
Custom values are designed for these cases that require context. They’re standard key-value pairs, in which the key is a string label of your choosing and the values are also strings, often evaluated from the build environment.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.value("my name", "my value");
-Dscan.value.<name>=<value>
For instance:
$ mvn package "-Dscan.value.CI Build Type=QA_Build"
<gradleEnterprise>
<buildScan>
<values>
<value>
<name>my name</name>
<value>my value</value>
</value>
</values>
</buildScan>
</gradleEnterprise>
pom.xml
<project>
...
<plugins>
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<buildScan>
<values>
<value>
<name>Build Number</name>
<value>${project.buildNumber}</value>
</value>
</values>
</buildScan>
</gradleEnterprise>
</configuration>
</plugin>
</plugins>
</project>
Prefer the programmatic access.
If specified in parent POM, links will be applied to all projects inheriting from it.
As with tags, you can filter build scans by custom values in Gradle Enterprise.
The Gradle Enterprise Maven extension imposes limits on captured custom values:
|
Callbacks
The Build Scan API allows to programmatically interact with the build scan configuration of the Gradle Enterprise Maven extension. Please see the Javadoc for the complete API documentation.
Executing operations at the end of the build
(Maven extension v1.2+)
What if you want to execute some code based on data that is only available late in the build? For example, you might want to label a build as "built-from-clean" if the clean
goal was run. But you don’t know if that’s the case until the goal execution plan is ready.
The Maven extension provides a buildFinished()
hook that you can use for these situations. It defers attaching custom data until the build has finished running. As an example, imagine you want to report how much disk space was taken up by the output directory. The build doesn’t know this until it’s finished, so the solution is to calculate the disk space and attach it to a custom value in the buildFinished()
hook:
import org.apache.commons.io.FileUtils;
import java.io.File;
...
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
File outputDir = mavenSession.getCurrentProject().getBuild().getOutputDirectory();
buildScan.buildFinished(buildResult -> buildScan.value("Disk usage (target dir)", FileUtils.sizeOfDirectory(outputDir)));
The buildFinished()
action has access to a BuildResult
instance that you can use to determine whether the build failed or not, like so:
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.buildFinished(buildResult -> {
buildResult.getFailures().stream().forEach(failure ->
buildScan.value("Failed with", failure.getMessage());
);
});
Executing operations when a build scan is published
(Maven extension v1.2+)
You might want to perform a custom operation when a build scan is published, like notifying an internal tool of your company. To do this , you can use the BuildScanApi#buildScanPublished()
method:
import java.nio.Files
...
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
File journal = new File("buildScans.txt")
buildScan.buildScanPublished(buildScan -> Files.write(journal.toPath(), buildScan.getBuildScanId().getBytes(), StandardOpenOption.APPEND));
Executing expensive operations
(Maven extension v1.2+)
Some data that you may wish to add to your build scan can be expensive to capture. For example, capturing the Git commit ID may require executing the git
command as an external process, which is expensive. To do this without slowing your build down, you can use the BuildScanApi#background()
method:
import java.io.File;
import org.eclipse.jgit.*;
...
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.background(api -> {
File projectDir = mavenSession.getCurrentProject().getBasedir();
Git git = Git.open(projectDir);
ObjectId objectId = git.getRepository().resolve("HEAD");
api.value("Git Commit ID", ObjectId.toString(objectId));
});
This method takes a function that will be executed on a separate thread, which allows Maven to continue without waiting for the expensive work to complete.
All background work will be completed before finishing the build and publishing the build scan.
Any errors that are thrown by the background action will be logged and captured in the build scan.
See the BuildScanApi#background() API reference for more information.
Obfuscating identifying data
(Maven extension v1.3.1+)
Build scans capture certain identifying information such as the operating system username, hostname and network addresses. You may choose to obfuscate this data so that it is not decipherable in build scans when viewed. To do this, you can use the BuildScanApi#obfuscation()
method.
The following examples show registering obfuscation functions for the different identifying data.
import java.util.stream.Collectors;
...
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScanApi.obfuscation(obfuscation -> obfuscation.username(s -> s.chars().mapToObj(c -> String.valueOf(Character.getNumericValue(c))).collect(Collectors.joining())));
import java.util.stream.Collectors;
...
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScanApi.obfuscation(obfuscation -> obfuscation.hostname(h -> h.chars().mapToObj(c -> String.valueOf(Character.getNumericValue(c))).collect(Collectors.joining())));
import java.util.stream.Collectors;
...
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScanApi.obfuscation(obfuscation -> obfuscation.ipAddresses(addresses -> addresses.stream().map(address -> "0.0.0.0").collect(Collectors.toList())));
See the BuildScanApi#obfuscation() API reference for more information.
Using the build cache
The build cache speeds up your builds by reusing outputs from any previous build, on any machine that is connected to the same build cache backend. It does this by reducing the inputs of a goal execution down to a strong hash key and storing the execution’s output under that key. It supports a local cache that allows other subsequent builds on the same machine to reuse the outputs whenever they execute a goal with the same inputs. The full benefit of the build cache is realized when also using the remote backend that Gradle Enterprise provides. This remote cache allows you to share cached outputs across your whole team, including local and CI builds.
Please refer to the build cache guide for step-by-step instructions on how to get started and in-depth explanations of important concepts. Moreover, the guide shows how to measure the effectiveness of the build cache in your project and explains how to roll out the build cache in your organization.
The build caching functionality for Maven requires a Gradle Enterprise license. The free |
Configuring the build cache
In order to use build caching for Apache Maven, you need to configure the location of your Gradle Enterprise server.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.setServer("https://gradle-enterprise.mycompany.com");
$ mvn package -Dgradle.enterprise.url=https://gradle-enterprise.mycompany.com
<gradleEnterprise>
<server>
<url>https://gradle-enterprise.mycompany.com</url>
</server>
</gradleEnterprise>
The precise URL you need depends on the hostname that your Gradle Enterprise instance has been configured with. If in doubt, be sure to ask whomever manages that instance.
You may encounter a warning about an untrusted certificate when connecting to Gradle Enterprise over HTTPS. The ideal solution is for someone to add a valid SSL certificate to the Gradle Enterprise instance, but we recognise that you may not be able to do that. In this case, set the allowUntrusted
option to true
:
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.setAllowUntrusted(true);
<gradleEnterprise>
<server>
<allowUntrusted>true</allowUntrusted>
</server>
</gradleEnterprise>
This is a convenient workaround, but you shouldn’t use it as a long-term solution.
Configuring the local cache
The extension uses a local build cache to store build outputs in the local filesystem. It prevents network roundtrips by storing both outputs that local builds created, as well as outputs that were downloaded from the remote build cache.
Disabling the local cache
The local build cache is enabled by default. This can be changed by setting the enabled
option to false
.
<gradleEnterprise>
<buildCache>
<local>
<enabled>false</enabled>
</local>
</buildCache>
</gradleEnterprise>
You can also use the -Dgradle.cache.local.enabled=false
system property to achieve the same effect.
Changing the local cache directory
The local cache is located at ${user.home}/.m2/.gradle-enterprise/build-cache
by default. This can be changed by setting the directory
option.
<gradleEnterprise>
<buildCache>
<local>
<directory>/path/to/local/build-cache</directory>
</local>
</buildCache>
</gradleEnterprise>
It is a common practice in large organizations to put the user home on a network share. Since the underlying motivation of a local build cache is to prevent network roundtrips, you should explicitly configure the local cache directory to a path on the local filesystem. |
Configuring local cache cleanup
To prevent the local cache from growing in size indefinitely, the local cache directory is cleaned up periodically. By default, the cleanup interval is 24 hours and the retention time is 7 days. The cleanup can be disabled by setting the enabled
option to false
.
<gradleEnterprise>
<buildCache>
<local>
<cleanup>
<enabled>false</enabled>
</cleanup>
</local>
</buildCache>
</gradleEnterprise>
The cleanup interval and retention time are controlled by the interval
and retention
options. The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS
.
<gradleEnterprise>
<buildCache>
<local>
<cleanup>
<retention>P30D</retention>
<interval>P10D</interval>
</cleanup>
</local>
</buildCache>
</gradleEnterprise>
Working offline
In order to work offline, the extension needs to have run in online mode at least once in the past 24 hours to check whether the given Gradle Enterprise server allows build caching for Maven. The result of this check is stored in a token in the user home. As long as you are working online, the token is refreshed every hour. The local cache will keep working in offline mode until that token expires after 24 hours.
Configuring the remote cache
Gradle Enterprise provides a cache node that is built into the server. Additionally, remote cache nodes can be spun up and connected to the server. By default, the built-in cache node of the Gradle Enterprise server is used.
Using a different cache node
The address of the remote cache node can be configured in the server
option.
<gradleEnterprise>
<buildCache>
<remote>
<server>
<url>http://my-node/cache/</url>
</server>
</remote>
</buildCache>
</gradleEnterprise>
Note that you still need to configure the address of your Gradle Enterprise server in the top-level server
option.
Similar to the top-level Gradle Enterprise server configuration, the remote cache server configuration also provides an
gradle-enterprise.xml
|
In order to use an authenticated cache node the credentials for that node have to be configured in the settings.xml
file. You can also use Maven’s password encryption feature to safely store these credentials.
<servers>
<server>
<id>my-node</id>
<username>username</username>
<password>password</password>
</server>
</servers>
The cache node with the ID my-node
can then be referenced in the remote cache configuration.
<gradleEnterprise>
<buildCache>
<remote>
<server>
<id>my-node</id>
<url>http://my-node/cache/</url>
</server>
</remote>
</buildCache>
</gradleEnterprise>
Disabling the remote cache
The remote build cache is enabled by default. This can be changed by setting the enabled
option to false
.
<gradleEnterprise>
<buildCache>
<remote>
<enabled>false</enabled>
</remote>
</buildCache>
</gradleEnterprise>
You can also use the -Dgradle.cache.remote.enabled=false
system property to achieve the same effect.
Enabling remote store
Since the remote build cache is shared with other developers and CI machines, storing in the remote cache is disabled by default. Storing outputs in the remote cache can be enabled by setting the storeEnabled
option to true
.
<gradleEnterprise>
<buildCache>
<remote>
<storeEnabled>true</storeEnabled>
</remote>
</buildCache>
</gradleEnterprise>
You can also use the -Dgradle.cache.remote.storeEnabled=true
system property to achieve the same effect.
In general, the remote cache should only be populated by controlled build environments such as CI servers. Therefore, the recommendation is to only enable it on the CI server. |
Rerunning goals
In rare circumstances the cache might be filled with an invalid entry, e.g. when another process deletes the outputs of a goal while the cache entry is being created. In this case you can use the -DrerunGoals
command line argument to rerun the goals and overwrite the faulty cache entry.
Normalization
The following snippet shows you how to ignore any file called META-INF/build.properties
on any runtime classpath in the given project. You can share this setting across many projects by putting it in the pluginManagement
section of your parent POM. You can use ANT-style patterns like META-INF/*/.sql
as well.
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<normalization>
<runtimeClassPath>
<ignoredFiles>
<ignoredFile>META-INF/build.properties</ignoredFile>
</ignoredFiles>
</runtimeClassPath>
</normalization>
</gradleEnterprise>
</configuration>
</plugin>
Adding inputs and outputs to a plugin or execution
You can fine-tune the inputs and outputs of goal executions in the <pluginManagement>
section of your pom.xml
file.
The following input normalization strategies are supported:
IGNORED_PATH |
Considers the full content of files, but ignores their path. |
NAME_ONLY |
Considers the full content of files, but only tracks their name and not the rest of their path. |
RELATIVE_PATH |
The default strategy. Considers the full content of a file, but only tracks their path relative to their root directory. The root directory is the directory that was added as an input. The path of that root directory itself is ignored. |
ABSOLUTE_PATH |
Considers the full content of files as well as their absolute path. Using this strategy is strongly discouraged, as the project directory (and thus all absolute paths) are usually different on different machines, which prevents cache hits. |
CLASSPATH |
Considers only the information relevant for running Java code. |
COMPILE_CLASSPATH |
Considers only the information relevant for compiling Java code. This means for example that only class files are considered and private implementation details like method bodies are ignored. |
The following will add the directory src/test/samples
as an input to all executions of the failsafe plugin. Any files in that folder which match the given includes and excludes will then be tracked as part of the cache key:
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<inputs>
<fileSets>
<fileSet>
<name>samples</name>
<paths>
<path>src/test/samples</path>
</paths>
<includes>
<include>/.sample</include>
</includes>
<excludes>
<exclude>archive//.sample</exclude>
</excludes>
<normalization>NAME_ONLY</normalization>
</fileSet>
</fileSets>
</inputs>
</plugin>
</plugins>
</gradleEnterprise>
</configuration>
</plugin>
You can also configure inputs and outputs for a specific execution of a plugin, without affecting other executions. The following will add the additional output directory ${project.build.directory}/schema
to the default-compile
execution of the compiler plugin. The contents of this directory will become part of the cache archive for this execution.
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<outputs>
<directories>
<directory>
<name>generatedSchema</name>
<path>${project.build.directory}/schema</path>
</directory>
</directories>
</outputs>
</execution>
</executions>
</plugin>
</plugins>
</gradleEnterprise>
</configuration>
</plugin>
Disabling build caching for a plugin or execution
You can disable caching on a fine-grained level in the <pluginManagement>
section of your pom.xml
file. The following will disable caching for all executions of the failsafe plugin in the given project:
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<outputs>
<notCacheableBecause>these tests verify integration with other systems and should rerun even if our inputs didn't change</notCacheableBecause>
</outputs>
</plugin>
</plugins>
</gradleEnterprise>
</configuration>
</plugin>
You can also disable caching for a specific execution. Other executions of that plugin will then still remain cacheable. The following will disable caching only for the systems-integration-test
execution of the failsafe plugin. Other tests will remain cacheable.
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>systems-integration-test</id>
<outputs>
<notCacheableBecause>these tests verify integration with other systems and should rerun even if our inputs didn't change</notCacheableBecause>
</outputs>
</execution>
</executions>
</plugin>
</plugins>
</gradleEnterprise>
</configuration>
</plugin>
Making other goals cacheable
The extension allows you to make any goal cacheable, beyond the ones that are supported out of the box. Take great care to define all of the goal’s inputs and outputs before doing so, to avoid false cache hits and follow-up errors.
When making goals cacheable, you don’t need to repeat the values of all their inputs and outputs. You can simply provide the name of each property and the extension will look up the value in the goal’s configuration. The extension will make sure that you have handled all configuration parameters of the goal in this way. If some parameter is irrelevant for the purposes of caching, e.g. because it only affects console output, you can tell the extension to ignore it.
For input properties, the extension supports all primitives, Strings, Enums and Collections, Arrays and Maps of those. Any other types need to be broken down using the nestedProperties
(for a single complex type) or iteratedProperties
(for a Collection of complex types) configuration.
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise>
<plugins>
<plugin>
<groupId>my.company</groupId>
<artifactId>awesome-but-slow-plugin</artifactId>
<inputs>
<fileSets>
<fileSet>
<name>sources</name>
<includesProperty>includes</includesProperty>
<excludesProperty>excludes</excludesProperty>
</fileSet>
</fileSets>
<properties>
<property>
<name>encoding</name>
</property>
</properties>
<ignoredProperties>
<ignore>logWarnings</ignore>
</ignoredProperties>
<nestedProperties>
<property>
<name>forkOptions</name>
<inputs>
<properties>
<property>
<name>maxHeap</name>
</property>
</properties>
</inputs>
</property>
</nestedProperties>
<iteratedProperties>
<property>
<name>targetPlatforms</name>
<inputs>
<properties>
<property>
<name>architecture</name>
</property>
<property>
<name>linkingMode</name>
</property>
</properties>
</inputs>
</property>
</iteratedProperties>
</inputs>
<outputs>
<directories>
<directory>
<name>outputDir</name>
</directory>
</directories>
<cacheableBecause>this plugin has CPU-bound goals with well-defined inputs and outputs</cacheableBecause>
</outputs>
</plugin>
</plugins>
</gradleEnterprise>
</configuration>
</plugin>
Solving problems with build caching
While working with the build cache you may encounter situations where build results are not retrieved from cache although you would expect them to. This section provides guidance for analyzing and solving these problems.
Debugging cache operations
The extension provides several loggers to make analyzing problems with build caching easier. To show the effective cache configuration, use the gradle.goal.cache
logger:
$ mvn clean verify -Dorg.slf4j.simpleLogger.log.gradle.goal.cache=debug [DEBUG] Using the build cache with the following configuration: Local build cache: enabled directory: /Users/johndoe/.m2/.gradle-enterprise/build-cache cleanup: enabled retention: 168h interval: 24h Remote build cache: enabled url: https://my-server/cache/ authenticated: false storeEnabled: false allowUntrustedServer: false
The gradle.goal.cache
logger will also print the result of determining the cacheability of the executed goals:
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-build-scan-extension-sample --- [INFO] skip non existing resourceDirectory /Users/johndoe/workspace/maven-build-scan-quickstart/src/main/resources [DEBUG] Build caching was not enabled for this goal execution because the 'resources' goal was not supported. [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ maven-build-scan-extension-sample --- [DEBUG] Local cache miss [DEBUG] Remote cache miss [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to /Users/johndoe/workspace/maven-build-scan-quickstart/target/classes [DEBUG] Stored outputs in the local build cache
All information printed by the |
Finding the cause of cache misses
Sometimes you might encounter a situation where a goal execution is not avoided by using the cache although you would expect it to be. For example, if you run the same build twice without any changes, the outputs of all supported goals should be retrieved from the local build cache (if it is enabled). If this is not the case, this almost always is caused by unstable inputs, e.g. a timestamp being added to a file by some build logic. In order to identify which inputs change between builds the Maven build comparison feature can be used. Simply run the same Maven build twice and compare those two builds. To make it easier to find unstable input files capturing of goal input files should be explicitly enabled using -Dgradle.scan.captureGoalInputFiles=true
Capturing goal input files has an impact on build performance. For this reason it is disabled by default. |
Once the build scans have been published they can be compared in Gradle Enterprise. In this example, the build was configured to write a timestamp to the build.properties
file. When comparing the two builds this shows up nicely in the comparison.

Once the changing input is identified, the build can be changed to be reproducable or normalization can be used to ignore the changing input.
Solving common causes of cache misses
Some widely used Maven plugins are a common cause of cache misses because they produce changing build results. This chapter shows you how to solve them.
JAXB
Old versions of the XJC binding compiler generate classes with methods in random order on each invocation. This has been fixed in JAXB 2.2.11. Since there are several Maven plugins available for JAXB, you need to find out which release of the plugin you are using includes the fixed JAXB release. For example the jaxb2-maven-plugin includes the fix starting from release 2.1.
Another cause of unstable build results when using JAXB is the fact that the XJC binding compiler generates a header containing a timestamp into all Java classes. This behavior is controlled by the --no-header
option which is false
by default (= always generate a header). To prevent this add the corresponding configuration to the Maven plugin you use, for example:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.4</version>
<configuration>
<noGeneratedHeaderComments>true</noGeneratedHeaderComments>
</configuration>
</plugin>
If you’re using the maven-jaxb2-plugin it’s still a good idea to remove unnecessary instability from its outputs. However, the Gradle Enterprise Maven Extension supports caching its generate
goal even if file headers are being generated. Thus, downstream goals will not be affected by its changing outputs when they are loaded from the cache.
Some of the Maven JAXB plugins generate code based on the current system’s locale if not configured otherwise. This leads to unstable outputs depending on the configuration of the machine that executes the build, which in turn can lead to cache misses. For this reason the locale to use during code generation should be explicitly configured. Both the jaxb2-maven-plugin and the maven-jaxb2-plugin provide a <locale>
option for this.
maven-bundle-plugin
By default the maven-bundle-plugin generates a timestamp the MANIFEST.MF
file. To prevent this, the configuration of the plugin has to be adjusted:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
<instructions>
<_removeheaders>Bnd-LastModified</_removeheaders>
</instructions>
</configuration>
</plugin>
Note that the underscope in <_removeheaders>
is not a typo.
maven-resources-plugin
A common pattern is to write a build timestamp to a build.properties
file using Maven resource filtering. One way to fix this is using Normalization to ignore the file. Alternatively the build can be adjusted by moving the generation of the timestamp to a separate profile that is only executed when creating a release:
build.timestamp=${timestamp}
<properties>
<timestamp>2019-03-07 12:00:00.000</timestamp>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<profile>
<id>release</id>
<properties>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss.S</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
</properties>
</profile>
maven-failsafe-plugin
The maven-failsafe-plugin provides two goals: integration-test
and verify
. The former runs in the integration-test
phase of the build and writes its results to a summary file. The latter runs in the verify
phase, reads the summary file, and fails the build in case of test failures.
When configuring multiple Failsafe executions, they use the same output location for the summary file by default. This will prevent all but the first execution of the integration-test
goal to be cacheable due to overlapping outputs. In order to get cache hits for all executions, you should configure a different summary file for each of them:
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>first-execution</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<!-- ... -->
<summaryFile>${project.build.directory}/failsafe-reports/first-failsafe-summary.xml</summaryFile>
</configuration>
</execution>
<execution>
<id>second-execution</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<!-- ... -->
<summaryFile>${project.build.directory}/failsafe-reports/second-failsafe-summary.xml</summaryFile>
</configuration>
</execution>
</executions>
</plugin>
If you have configured multiple executions that execute the same or an overlapping set of test classes (e.g. with different parameters), you should in addition change the reports directory, for example:
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>first-execution</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<!-- ... -->
<reportsDirectory>${project.build.directory}/first-failsafe-reports</reportsDirectory>
<summaryFile>${project.build.directory}/first-failsafe-reports/failsafe-summary.xml</summaryFile>
</configuration>
</execution>
<execution>
<id>second-execution</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<!-- ... -->
<reportsDirectory>${project.build.directory}/second-failsafe-reports</reportsDirectory>
<summaryFile>${project.build.directory}/second-failsafe-reports/failsafe-summary.xml</summaryFile>
</configuration>
</execution>
</executions>
</plugin>
Slow resolution of host name
Build scans attempt to determine the host name of the machine. An issue affecting macOS can cause a delay when doing this in some environments.
If you see a warning during your build that resolving the local host name is slow, you can workaround the problem by adding a host name mapping to your /etc/hosts
file.
Add these lines to your /etc/hosts
file, substituting your computer name for 'mbpro' in the below snippet:
127.0.0.1 localhost mbpro.local
::1 localhost mbpro.local
Appendix A: Configuration reference
gradle-enterprise.xml
Most aspects of the Gradle Enterprise Maven extension are configured in the gradle-enterprise.xml
configuration file. Some of the options can be overwritten by system properties.
The gradle-enterprise.xml
file can be put into several locations. These files are merged and their properties overwritten based on the precedence rules below:
-
<maven-home>/conf/gradle-enterprise.xml
is used to set global defaults for a given Maven installation. This is useful when you ship a custom Maven distribution to your teams. The location of this configuration file can be overwritten using the-Dgradle.global.config
argument. This can be useful for CI environments where changing the Maven installation is not possible. -
<project-dir>/.mvn/gradle-enterprise.xml
is used for project-specific configuration and overrides the global configuration. -
<user-home>/.m2/gradle-enterprise.xml
is used for user-specific configuration and overrides the project configuration. The location of this configuration file can be overwritten using the-Dgradle.user.config
argument. This can be useful for CI environments where changing the user home is not possible.
The example below shows a full reference of everything you can configure in this file.
Be sure to include the XML namespace declarations to get auto-completion in your IDE. You can get a specific schema version by appending the version to the schema location, e.g. https://www.gradle.com/schema/gradle-enterprise-maven-1.0.xsd. IntelliJ IDEA will mark unknown schemas as missing and they have to be explicitly fetched via the quick fix dialog (Alt + Enter). There is an open issue to make this more user friendly. |
<gradleEnterprise
xmlns="https://www.gradle.com/gradle-enterprise-maven" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.gradle.com/gradle-enterprise-maven https://www.gradle.com/schema/gradle-enterprise-maven.xsd">
<server>
<!-- ID used to reference an element in the settings.xml -->
<id>my-server</id>
<!-- Address of the Gradle Enterprise server. System property is 'gradle.enterprise.url'. -->
<url>http://my-server/</url>
<!-- Whether untrusted connections to the Gradle Enterprise server should be accepted. Defaults to false. -->
<allowUntrusted>false</allowUntrusted>
</server>
<buildScan>
<!-- Behavior of publishing build scans. Possible values are ALWAYS, ON_FAILURE, ON_DEMAND. Defaults to ALWAYS. -->
<publish>ALWAYS</publish>
<!-- Terms of service acceptance (mandatory to publish to scans.gradle.com) -->
<termsOfService>
<!-- Address of the terms of service. Must be 'https://gradle.com/terms-of-service'. Defaults to an empty string. -->
<url></url>
<!-- Signal acceptance of the terms of service. Must be 'true'. Defaults to false. -->
<accept>false</accept>
</termsOfService>
<!-- Whether to capture content hashes of each input file for build scan comparison. Defaults to false. System property is 'gradle.scan.captureGoalInputFiles' -->
<captureGoalInputFiles>true</captureGoalInputFiles>
<!-- List of tags to capture -->
<tags>
<tag>my tag</tag>
</tags>
<!-- List of links to capture -->
<links>
<link>
<name>my link</name>
<url>http://my-site.com</url>
</link>
</links>
<!-- List of custom values to capture -->
<values>
<value>
<name>my name</name>
<value>my value</value>
</value>
</values>
</buildScan>
<buildCache>
<!-- Local cache configuration -->
<local>
<!-- Whether the local cache is enabled. Defaults to true. System property is 'gradle.cache.local.enabled'. -->
<enabled>true</enabled>
<!-- Local cache directory. Defaults to ${user.home}/.m2/.gradle-enterprise/build-cache. System property is 'gradle.cache.local.directory'. -->
<directory>/some/other/location</directory>
<!-- Local cache cleanup configuration -->
<cleanup>
<!-- Whether local cache cleanup is enabled. Defaults to true. System property is 'gradle.cache.local.cleanup.enabled'. -->
<enabled>true</enabled>
<!-- Items in the cache that were not used in this period will be deleted. Defaults to P7D. System property is 'gradle.cache.local.cleanup.retention'. -->
<retention>P30D</retention>
<!-- Interval at which the cleanup occurs. Defaults to P1D. System property is 'gradle.cache.local.cleanup.interval'. -->
<interval>P10D</interval>
</cleanup>
</local>
<!-- Remote cache configuration -->
<remote>
<!-- Remote cache server configuration -->
<server>
<!-- Optionally use the ID of a server specified in your settings.xml to use its credentials. System property is 'gradle.cache.remote.serverId'. -->
<id>remote-cache</id>
<!-- URL of the remote cache. Defaults to ${gradle.enterprise.url}/cache/. System property is 'gradle.cache.remote.url'. -->
<url>http://my-node/cache/</url>
<!-- Whether the remote cache accepts untrusted connections. Defaults to false. System property is 'gradle.cache.remote.allowUntrustedServer'. -->
<allowUntrusted>true</allowUntrusted>
</server>
<!-- Whether the remote cache is enabled. Defaults to true. System property is 'gradle.cache.remote.enabled'. -->
<enabled>true</enabled>
<!-- Whether to store outputs in the remote build cache (as opposed to only loading from it). Defaults to false. System property is 'gradle.cache.remote.storeEnabled'. -->
<storeEnabled>true</storeEnabled>
</remote>
</buildCache>
</gradleEnterprise>
In order to use an authenticated Gradle Enterprise server, the credentials for that server have to be configured in the settings.xml
file. You can also use Maven’s password encryption feature to safely store these credentials.
<servers>
<server>
<id>my-server</id>
<username>username</username>
<password>password</password>
</server>
</servers>
pom.xml
The Gradle Enterprise Maven extension also allows you to configure module-specific aspects in the corresponding pom.xml
file. This allows you to share common configuration between your project by putting it in a parent POM. See the example below for a full reference.
In order to get auto-completion in your IDE, be sure to include the XML namespace and schema location as shown in the example below. You can get a specific schema version by appending the version to the schema location, e.g. https://www.gradle.com/schema/gradle-enterprise-maven-project-1.0.xsd. IntelliJ IDEA will mark unknown schemas as missing and they have to be explicitly fetched via the quick fix dialog (Alt + Enter). There is an open issue to make this more user friendly. Please note that auto-completion is currently only supported by Eclipse. For IntelliJ IDEA, there’s an open issue to add such a feature. |
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd
https://www.gradle.com/gradle-enterprise-maven-project https://www.gradle.com/schema/gradle-enterprise-maven-project.xsd">
...
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
<configuration>
<gradleEnterprise xmlns="https://www.gradle.com/gradle-enterprise-maven-project">
<normalization>
<runtimeClassPath>
<ignoredFiles>
<ignoredFile>META-INF/build.properties</ignoredFile>
</ignoredFiles>
</runtimeClassPath>
</normalization>
<plugins>
<!-- an example of adding more details to an already cacheable plugin -->
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<inputs>
<fileSets>
<fileSet>
<name>samples</name>
<paths>
<path>src/test/samples</path>
</paths>
<includes>
<include>/.sample</include>
</includes>
<excludes>
<exclude>archive//.sample</exclude>
</excludes>
<normalization>NAME_ONLY</normalization>
</fileSet>
</fileSets>
</inputs>
<outputs>
<files>
<file>
<name>summary</name>
<path>target/test-results/summary.txt</path>
</file>
</files>
<directories>
<directory>
<name>screenshots</name>
<path>target/test-results/screenshots</path>
</directory>
</directories>
<notCacheableBecause>these tests verify integration with other systems and should rerun even if our
inputs didn't change
</notCacheableBecause>
</outputs>
<localState>
<fileSets>
<fileSet>
<name>someTemporaryStuff</name>
<paths>
<path>target/myTestFramework/tmp</path>
</paths>
</fileSet>
</fileSets>
</localState>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<inputs>
<!-- same as above -->
</inputs>
<outputs>
<!-- same as above -->
</outputs>
<localState>
<!-- same as above -->
</localState>
</execution>
</executions>
</plugin>
<!-- an example of making a custom plugin cacheable -->
<plugin>
<groupId>my.company</groupId>
<artifactId>awesome-but-slow-plugin</artifactId>
<inputs>
<fileSets>
<fileSet>
<name>sources</name>
<includesProperty>includes</includesProperty>
<excludesProperty>excludes</excludesProperty>
</fileSet>
</fileSets>
<properties>
<property>
<name>encoding</name>
</property>
</properties>
<ignoredProperties>
<ignore>logWarnings</ignore>
</ignoredProperties>
<nestedProperties>
<property>
<name>forkOptions</name>
<inputs>
<properties>
<property>
<name>maxHeap</name>
</property>
</properties>
</inputs>
</property>
</nestedProperties>
<iteratedProperties>
<property>
<name>targetPlatforms</name>
<inputs>
<properties>
<property>
<name>architecture</name>
</property>
<property>
<name>linkingMode</name>
</property>
</properties>
</inputs>
</property>
</iteratedProperties>
</inputs>
<outputs>
<directories>
<directory>
<name>outputDir</name>
</directory>
</directories>
<cacheableBecause>this plugin has CPU-bound goals with well-defined inputs and outputs</cacheableBecause>
</outputs>
<localState>
<fileSets>
<fileSet>
<name>tempDir</name>
</fileSet>
</fileSets>
</localState>
</plugin>
</plugins>
<buildScan>
<tags>
<tag>my tag</tag>
</tags>
<links>
<link>
<name>my link</name>
<url>http://my-site.com</url>
</link>
</links>
<values>
<value>
<name>Build Number</name>
<value>${project.buildNumber}</value>
</value>
</values>
</buildScan>
</gradleEnterprise>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
Appendix B: Programmatic configuration
Accessing the Build Scan API to perform programmatic configuration of build scans can be achieved in several ways, described below.
Using the Build Scan API in a Groovy script
You might for instance be interested to add custom tags, links or custom values only if a certain condition is met. You can achieve this, for example, by using the Groovy Maven Plugin, and define your logic in Groovy to conditionally add custom data through the exposed BuildScanApi
.
def buildScanApi = session.lookup("com.gradle.maven.extension.api.scan.BuildScanApi")
buildScanApi.executeOnce('custom-data') { api ->
api.tag('my project')
log.info('Custom data added')
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>my-project</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>build-scan-custom-data</id>
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>file:///${maven.multiModuleProjectDirectory}/customData.groovy</source>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.5.7</version>
<type>pom</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
Using the Build Scan API in a custom plugin
The Build Scan API can be accessed in a Mojo implementation by looking it up in the MavenSession
object. Your Mojo will need a compile dependency on the Gradle Enterprise Maven extension.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>build-scan-api-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<version>1.0</version>
<dependencies>
<!-- Maven Plugin API -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.6.3</version>
</dependency>
<!-- Maven Plugin Tool for Annotations -->
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
</dependency>
<!-- Maven Plugin plugin -->
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
</dependency>
<!-- Gradle Enterprise Maven extension -->
<dependency>
<groupId>com.gradle</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Bind the Maven Plugin plugin descriptor goal -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>default-descriptor</id>
<phase>process-classes</phase>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
You can then write your custom Mojo, such as:
import com.gradle.maven.extension.api.scan.BuildScanApi;
import java.util.function.Consumer;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
@Mojo(name = "custom-data")
public class BuildScanMojo extends AbstractMojo {
@Parameter(defaultValue = "${session}", readonly = true)
private MavenSession session;
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
public void execute() {
try {
var buildScanApi = (BuildScanApi) session.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScanApi.executeOnce("custom-data", new Consumer<BuildScanApi>() {
@Override
public void accept(BuildScanApi api) {
// Do something with the Build Scan API.
getLog().info("Custom data added");
}
});
} catch (ComponentLookupException e) {
getLog().error(e);
}
}
}
Then, you can publish it and reference it on the top-level project of a consuming project.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>my-consuming-project</artifactId>
<build>
<plugins>
<!-- Build scan API Maven plugin -->
<plugin>
<groupId>com.company</groupId>
<artifactId>build-scan-api-maven-plugin</artifactId>
<version>1.0</version>
</plugin>
</plugins>
</build>
</project>
Finally, you can reference it when invoking Maven.
mvn build-scan-api:custom-data
Running custom build scan configuration logic once
The build scan API offers a way to guarantee that some code is only executed once for the whole execution of a multi-project Maven build.
The Maven extension provides an executeOnce()
hook that you can use for these situations. It must be provided with an identifier, and can call any service provided by the API. The identifier is used to guarantee that the provided action will be executed at most once.
BuildScanApi buildScan = (BuildScanApi) mavenSession.lookup("com.gradle.maven.extension.api.scan.BuildScanApi");
buildScan.executeOnce("capture custom data", api -> api.tag("my custom tag")); // This will be executed once
buildScan.executeOnce("capture custom data", api -> api.tag("my other custom tag")); // This will not be executed and will silently be ignored
buildScan.executeOnce("publish to journal", api -> api.buildScanPublished(buildScan -> journalServer.add(buildScan))); // This will be executed once
Appendix C: Build scans captured information
The Gradle Enterprise Maven extension captures information while the build is running and transmits it to a server after the build has completed.
Most of the information captured can be considered to be build data. This includes the name of the projects in your build, the executed goals, plugins and other things of this nature. Some more general environmental information is also captured. This includes your Java version, operating system, hardware, country, timezone and other things of this nature.
Notably, the actual source code being built and the output artifacts are not captured. However, error messages emitted by compilers or errors in tests may reveal aspects of the source code.
Listing
The list below details the notable information captured by the Gradle Enterprise Maven extension and transmitted in a build scan.
-
Environment
-
Username (system property
'user.name'
) -
Local hostname (environment variable
'COMPUTERNAME'
/'HOSTNAME'
) -
Public hostname
-
Local IP addresses
-
-
Build
-
Maven command-line and invocation options (e.g. requested phases and goals, switches)
-
Build failure exception messages and stacktraces
-
Build console output
-
Build cache configuration
-
Access
Build scans published to a Gradle Enterprise installation are viewable by all users that can reach the server and have the required roles, should Identity Access Management (IAM) be turned on. Gradle Enterprise provides a search interface for discovering and finding individual build scans.
Build scans published to scans.gradle.com are viewable by anyone with the link assigned when publishing the build scan. Links to individual build scans are not discoverable and cannot be guessed, but may be shared.
Appendix D: Cacheable plugins and goals
The extension caches the following plugins and goals out of the box. Unless otherwise noted, all their parameters are tracked as part of the cache key.
maven-compiler-plugin
Supported versions: 3.1 and above
Supported goals:
-
compile
-
testCompile
Caching is automatically disabled if:
-
a non-javac toolchain is used
The following use cases currently require disabling the cache for this plugin:
-
using annotation processors that read files outside of Maven’s resource directories
-
using annotation processors that generate sources outside of Maven’s generated sources directory
-
using any non-deterministic annotation processors
Compile avoidance
Unless there are annotation processors on the classpath, the extension uses compile avoidance so your sources are only recompiled if the signatures of the classes on the compile classpath have changed.
maven-surefire-plugin and maven-failsafe-plugin
Supported versions: 2.12.4 and above
Supported goals:
-
surefire:test
-
failsafe:integration-test
Caching is automatically disabled if:
-
<debugForkedProcess>
is true -
<basedir>
is changed to something different from the project’s directory -
<workingDirectory>
is changed to something different from the project’s root dir
The following use cases currently require disabling the cache for these plugins:
-
non-deterministic tests (e.g. tests with random parameters)
-
tests that read files that are not on the test classpath (e.g.
new File("src/test/samples")
) -
tests that write additional results that you absolutely need (e.g. screenshots for failed UI tests)
-
tests that read environment variables that are not explicitly declared using the
<environmentVariables>
property -
tests that use Java agents that read additional inputs or create additional outputs, except for JaCoCo, which is explicitly supported
The following properties are deliberately not tracked as inputs, because they should not influence the test result:
-
all concurrency settings, e.g.
<threadCount>
jacoco-maven-plugin
Supported versions: 0.5 and above
Supported goals:
-
none of JaCoCo’s own goals are cached
-
surefire and failsafe remain cacheable when JaCoCo is used
Caching is automatically disabled if:
-
<append>
is true
The JaCoCo plugin hooks into surefire and failsafe as a Java agent. The extension automatically tracks all JaCoCo agent options when determining the cache key for surefire and failsafe tests. The JaCoCo execution data file is cached as an additional output of the test execution.
maven-jaxb2-plugin (org.jvnet.jaxb2.maven2)
Supported plugin artifact ids:
-
maven-jaxb2-plugin
-
maven-jaxb20-plugin
-
maven-jaxb21-plugin
-
maven-jaxb22-plugin
-
maven-jaxb23-plugin
Supported versions: 0.12.3 and above
Supported goals:
-
generate
Caching is automatically disabled if:
-
a non-local URL is used to declare a catalog, schema, or binding
The following use cases currently require disabling the cache for these plugins:
-
non-local URL references to schemas or bindings with changing content from within schema or binding files
The following properties are deliberately not tracked as inputs, because they should not influence the result of code generation:
-
logging settings (
verbose
) -
proxy settings (
proxyHost
,proxyPort
,proxyUsername
,proxyPassword
,useActiveProxyAsHttpproxy
) -
settings for the plugin’s up-to-date check and incremental build feature (
forceRegenerate
,removeOldOutput
,produces
,cleanPackageDirectories
)
maven-javadoc-plugin
Supported versions: 2.7 and above
Supported goals:
-
javadoc:javadoc
-
javadoc:javadoc-no-fork
-
javadoc:test-javadoc
-
javadoc:test-javadoc-no-fork
-
javadoc:jar
-
javadoc:test-jar
Caching is automatically disabled if:
-
<skip>
is true
The following properties are deliberately not tracked as inputs, because they should not influence the javadoc output:
maven-checkstyle-plugin
Supported versions: 2.14 and above
Supported goals:
-
check
-
checkstyle
-
checkstyle-aggregate
Caching is automatically disabled if:
-
<skip>
is true
The following properties are deliberately not tracked as inputs, because they do not influence the outcome of the goal:
Absolute paths in output files are ignored
Checkstyle’s output files contain absolute paths that are deliberately ignored by the extension. Thus, when loading the goal’s outputs from cache, the referenced paths might not exist on the machine that is executing the build. In case that’s problematic for you, please disable the cache for this goal. |
Appendix E: Anatomy of the .gradle-enterprise directory
By default, the Gradle Enterprise Maven extension stores temporary data in the ${user.home}/.m2/.gradle-enterprise
directory. The location can be customized by setting the gradle.enterprise.storage.directory
system property:
$ mvn clean verify -Dgradle.enterprise.storage.directory=/path/to/.gradle-enterprise
The directory may contain the following subdirectories:
- build-cache
-
Location of the local build cache
- build-cache-tmp
-
Temporary directory for loading and storing entries in the remote build cache in case the local build cache is disabled
- build-scan-data
-
Data collected to create build scans
- token-cache
-
Location of the entitlement tokens
The |
Appendix F: Gradle Enterprise Maven Extension release history
1.3.2 - 13th December 2019
-
Test capturing is disabled when tests are executed with Java < 8
-
Improved help message when authentication is required for build scan publishing
-
Mitigation if slow local host name resolution on macOS
1.3.1 - 9th December 2019
-
Users can provide obfuscation functions for captured username, local IP addresses and hostnames
-
String interpolation in Surefire
argLine
property is supported
1.3 - 25th November 2019
-
Add support for authenticated build scans feature
-
Add support for maven-javadoc-plugin 3.2.0
-
Add support for maven-surefire-plugin and maven-failsafe-plugin 3.0.0-M4
-
Logs on debug level are not captured
-
Ignore additional Maven extension applications, when another instance is already applied
1.2.8 - 16th October 2019
-
Global
gradle-enterprise.xml
is now read from${maven.home}/conf
in Maven < 3.5.0 -
Fixed hanging build due to race condition when an error during data capturing occurred
1.2.7 - 1st October 2019
-
Extension behaves more lenient towards unusual embeddings of Maven
-
Goal cache key only contains the major Java version
1.2.6 - 18th September 2019
-
Reduced runtime and memory allocation overhead of test capturing
1.2.5 - 16th September 2019
-
Fixed project structure capturing when a goal is executed very early in the build
-
Fixed test capturing when a custom test provider is specified
1.2.4 - 10th September 2019
-
Fixed test capturing when test started event cannot be found
-
Fixed test capturing when duplicate JUnit 4 Descriptions are found
1.2.3 - 28th August 2019
-
Add
executeOnce
Maven build scan API -
Fixed concurrency issue in test capturing
-
Handle multi-threading and fail-fast scenarios for Maven < 3.6.2
-
Handling of Maven workspace ID is enhanced
1.2.2 - 23rd August 2019
-
TestNG tests handle orphaned failure events
-
Fixed build hangs when failure occurred during test processing
-
Fixed exceptions in test capturing
1.2.1 - 20th August 2019
-
Support failing
@Before*
/@After*
annotations in all supported test frameworks -
Support failing TestNG
dependOnMethods
anddependsOnGroups
tests -
Don’t fail when
null
test method name or test class name is encountered -
Gradle Enterprise server set by configuration is now retrievable from the
BuildScanApi
1.2 - 8th August 2019
-
Capture tests executed in Surefire/Failsafe 2.15+ for JUnit 4/5 and TestNG frameworks
-
Capture console output
-
Add adjacent build scans support for Maven by capturing the workspace ID, unique per project workspace
-
Fixed origin build scan link when there is no reference to the output producing goal
-
Fixed project structure capturing when other extensions/plugins update the internal Maven projects
-
Fixed event serialization error when using custom tags/links/values via the
BuildScanApi
-
Enhanced the
BuildScanApi
:-
Register a background action
-
Register an action to be done as late as possible before publishing
-
Register an action to be done when a build scan is published
-
Set terms of service params programmatically
-
Set server programmatically
-
Set if untrusted servers are allowed programmatically
-
Specify the publication behaviour programmatically
-
Specify whether goal input files should be captured programmatically
-
1.1.4 - 28th June 2019
-
Undeclared inputs are reported correctly on Windows
-
Registering additional inputs/outputs works in the presence of mixed line separators
1.1.3 - 20th June 2019
-
Add support for upcoming
maven-surefire-plugin/maven-failsafe-plugin
version3.0.0-M4
-
Build flags capturing is not based on the
MAVEN_CMD_LINE_ARGS
environment variable anymore -
Add support for
mojo-executor
Maven plugin -
Failed
maven-failsafe
tests are no longer cached
1.1.2 - 17th May 2019
-
Fixed a bug which resulted in wrong event order caused by system clock adjustments
-
Fix handling of
**
in include patterns of supported goals and custom input declarations
1.1.1 - 12th May 2019
-
Read project-specific
gradle-enterprise.xml
from same.mvn
directory that Maven uses to readextensions.xml
1.1 - 3rd May 2019
-
Output of
javadoc:aggregate
is cached for maven-javadoc-plugin:2.7+ -
Output of
javadoc:aggregate-jar
is cached for maven-javadoc-plugin:2.7+ -
Output of
javadoc:aggregate-no-fork
is cached for maven-javadoc-plugin:2.7+ -
Output of
javadoc:test-aggregate
is cached for maven-javadoc-plugin:2.7+ -
Output of
javadoc:test-aggregate-jar
is cached for maven-javadoc-plugin:2.7+ -
Output of
javadoc:test-aggregate-no-fork
is cached for maven-javadoc-plugin:2.7+ -
Maven log no longer disappears when extension is applied twice
-
Skipped cacheable goals are reported as "skipped" instead of "not cacheable"
-
Local cache issues fail the build instead of logging a warning
-
Extension only snapshots input files that match given include/exclude patterns
-
Fix
NullPointerException
when trying to resolve a non-existing plugin without version -
Parallel forked goal executions are captured correctly in build scans
-
Capture finer-grained fingerprint events
1.0.8 - 18th April 2019
-
Fix handling of null-valued system properties.
-
Prevent non existing javadoc jars from being attached.
-
java.io.tmpdir is ignored in surefire systemProperties/systemPropertyVariables.
1.0.7 - 17th April 2019
-
Output of
javadoc:test-jar
is cached for maven-javadoc-plugin:2.7+. -
Output of
javadoc:jar
is cached for maven-javadoc-plugin:2.7+. -
Output of
javadoc:test-javadoc-no-fork
is cached for maven-javadoc-plugin:2.7+. -
Output of
javadoc:test-javadoc
is cached for maven-javadoc-plugin:2.7+. -
Output of
javadoc:javadoc-no-fork
is cached for maven-javadoc-plugin:2.7+. -
Output of
javadoc:javadoc
is cached for maven-javadoc-plugin:2.7+. -
Output of
generate
goal is cached for maven-jaxb2-plugin:0.12.3+. -
Output of
checkstyle:check
is cached for maven-checkstyle-plugin:2.14+. -
Output of
checkstyle:checkstyle
is cached for maven-checkstyle-plugin:2.14+. -
Output of
checkstyle:checkstyle-aggregate
is cached for maven-checkstyle-plugin:2.14+. -
Protect against double applications.
-
Protect against event notifications received before initialization (workaround for MNG-6619).
-
Handle more absolute paths in JVM arguments out of the box.
-
Broken
java
executables make goals non-cacheable instead of failing the build. -
System properties now overwrite values in
gradle-enterprise.xml
. -
Allow same file to appear in multiple output locations (e.g. the
summaryFile
of the maven-failsafe-plugin). -
User can add additional inputs and outputs to goal executions using the
pom.xml
DSL. -
Command line arguments are normalized, removing all known input and output paths from them to allow relocation.
-
Caching is deactivated for goals that contain undeclared file paths in their input properties (e.g. JVM args).
1.0.6 - 26th March 2019
-
Add support for code completion of
publishMode
ingradle-enterprise.xml
. -
Support broken JARs in annotation processor detection.
1.0.5 - 20th March 2019
-
Only fingerprints for external jars are stored in the build cache.
-
Surefire statistics file and tempDir are tracked as local state.
-
Performance improvements for jar fingerprinting and cache load operations.
-
More helpful error message for invalid build scan publishing mode configuration.
1.0.4 - 15th March 2019
-
Fix ID generation for build cache events.
-
Jar fingerprints are stored in the build cache.
1.0.3 - 12th March 2019
-
Fix hashing of build cache operations, that was leading to event ID collisions.
-
Support a proxy server when publishing build scans.
-
Prevent publishing build scans when there is no entitlement.
1.0.2 - 6th March 2019
-
Runtime classpath normalization recursively inspects WAR, EAR, ZIP and APK files.
-
Track
runOrder
as input property for themaven-surefire-plugin
andmaven-failsafe-plugin
. -
Fix project capturing to respect
<module>
declarations.
1.0.1 - 1st March 2019
-
Restore compatibility with Maven <
3.5.2
. -
Enhance console output of terms of services.
1.0 - 28th February 2019
Initial Release.
Appendix G: Gradle Enterprise Maven Extension compatibility with Apache Maven and Gradle Enterprise
Compatibility between versions of Apache Maven, Gradle Enterprise, and the Gradle Enterprise Maven extension can be found here.