---
component: maven
version: "2.4"
slug: maven/maven-extension
canonical_url: "https://docs.gradle.com/develocity/maven/2.4/maven-extension/"
title: "Develocity Maven Extension User Manual"
description: "Comprehensive guide for using the Develocity Maven Extension."
keywords: []
status: current
---

> Canonical: <https://docs.gradle.com/develocity/maven/2.4/maven-extension/>

<!-- llms-index: https://docs.gradle.com/develocity/llms.txt -->

# Develocity Maven Extension User Manual

<a id="preamble"></a>

The Develocity Maven extension enables integration with [Develocity](https://gradle.com) and [gradle.com/scans/maven/](https://gradle.com/scans/maven/).

> [!NOTE]
> Important Note for (Legacy) Gradle Enterprise Maven Extension Users: If you are using the (Legacy) Gradle Enterprise Maven extension, be aware that it’s no longer maintained. We strongly recommend migrating to the Develocity extension. For detailed instructions, refer to the Migrating to the Develocity extension section.

<a id="getting_set_up"></a>

## Getting Set Up

<a id="automated-setup"></a>

### Automated Setup

Execute the `init` goal to quickly set up the Develocity Maven extension on a Maven project:

```shell
mvn com.gradle:develocity-maven-extension:2.4.0:init -Ddevelocity.url=https://develocity.example.com
```

(function() { // Wait for DOM to be ready function ready(fn) { if (document.readyState !== 'loading') { fn(); } else { document.addEventListener('DOMContentLoaded', fn); } } ready(function() { // Find all collapsible togglers const togglers = document.querySelectorAll('.collapsible-toggler'); togglers.forEach(function(toggler) { toggler.addEventListener('click', function(e) { e.preventDefault(); const targetId = this.getAttribute('data-target'); const targetElement = document.getElementById(targetId); if (targetElement) { const wasHidden = targetElement.classList.contains('hidden'); // Toggle visibility with smooth animation if (wasHidden) { targetElement.classList.remove('hidden'); this.setAttribute('aria-expanded', 'true'); // Update icon to point down const icon = this.querySelector('.toggle-icon'); if (icon) icon.textContent = '▼'; // Add a brief highlight to show what expanded const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark'; const highlightColor = isDarkMode ? 'var(--background-subtle)' : 'var(--panel-background)'; targetElement.style.background = highlightColor; targetElement.style.transition = 'background-color 0.3s ease'; setTimeout(() => { targetElement.style.background = ''; }, 300); } else { targetElement.classList.add('hidden'); this.setAttribute('aria-expanded', 'false'); // Update icon to point right const icon = this.querySelector('.toggle-icon'); if (icon) icon.textContent = '▶'; } // Optional: Scroll to keep the toggler in view if content is large if (wasHidden && targetElement.offsetHeight > window.innerHeight / 2) { setTimeout(() => { toggler.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 100); } } }); // Add keyboard support (Enter and Space) toggler.addEventListener('keydown', function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.click(); } }); // Set initial aria-expanded state and add accessibility attributes const targetId = toggler.getAttribute('data-target'); const targetElement = document.getElementById(targetId); if (targetElement) { const isHidden = targetElement.classList.contains('hidden'); toggler.setAttribute('aria-expanded', isHidden ? 'false' : 'true'); toggler.setAttribute('role', 'button'); toggler.setAttribute('tabindex', '0'); toggler.setAttribute('aria-controls', targetId); // Add a title for better UX const actionText = isHidden ? 'Click to expand' : 'Click to collapse'; toggler.setAttribute('title', toggler.textContent.trim() + ' - ' + actionText); // Debug: Add a data attribute to verify state toggler.setAttribute('data-debug-state', isHidden ? 'collapsed' : 'expanded'); // Set initial icon state const icon = toggler.querySelector('.toggle-icon'); if (icon) { icon.textContent = isHidden ? '▶' : '▼'; } } }); }); })(); .collapsible-toggler { background: none; border: none; color: var(--heading-font-color); cursor: pointer; text-decoration: none; padding: 2px 4px; font: inherit; display: inline-flex; align-items: center; gap: 6px; border-radius: 3px; transition: all 0.2s ease; position: relative; } .collapsible-toggler:hover { color: var(--body-font-color); background-color: var(--panel-background); } .collapsible-toggler:focus { outline: 2px solid var(--heading-font-color); outline-offset: 2px; } /\* Dark mode support \*/ :root\[data-theme="dark"\] .collapsible-toggler { color: var(--text); } :root\[data-theme="dark"\] .collapsible-toggler:hover { color: var(--text); background-color: var(--background-subtle); } :root\[data-theme="dark"\] .collapsible-toggler:focus { outline-color: var(--text); } /\* Style the toggle icon \*/ .toggle-icon { font-size: 0.8em; transition: all 0.2s ease; display: inline-block; margin-left: 4px; } .toggle-text { margin-right: 4px; } .collapsible-content.hidden { display: none; } .collapsible-content { margin-top: 0.5em; transition: opacity 0.2s ease; } /\* Add subtle animation when showing content \*/ .collapsible-content:not(.hidden) { animation: slideIn 0.2s ease-out; } @keyframes slideIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } } /\* Additional visual cue - add a subtle border when hovering over collapsible sections \*/ .collapsible-toggler:hover + .collapsible-content, .collapsible-content:hover { border-left: 2px solid var(--panel-border-color); padding-left: 8px; transition: all 0.2s ease; } /\* Add a subtle indicator that content is collapsible \*/ .collapsible-content { position: relative; } .collapsible-content:not(.hidden)::before { content: ''; position: absolute; left: -8px; top: 0; bottom: 0; width: 2px; background: linear-gradient(to bottom, var(--panel-border-color), transparent); border-radius: 1px; } /\* Add visual feedback on hover \*/ .collapsible-toggler:hover::after { color: var(--body-font-color); transform: scale(1.1); } /\* Dark mode support for visual elements \*/ :root\[data-theme="dark"\] .collapsible-toggler:hover + .collapsible-content, :root\[data-theme="dark"\] .collapsible-content:hover { border-left-color: var(--border); } :root\[data-theme="dark"\] .collapsible-content:not(.hidden)::before { background: linear-gradient(to bottom, var(--border), transparent); } :root\[data-theme="dark"\] .collapsible-toggler:hover::after { color: var(--text); }

See [here](#init) for more information.

<a id="manual-setup"></a>

### Manual Setup

Apply the Develocity Maven extension to your build by adding the following configuration block to a new or existing `.mvn/extensions.xml` file in your Maven project. Maven automatically downloads the extension from [Maven Central](https://central.sonatype.com/search?q=a:develocity-maven-extension) when you run your build.

**Add the following to .mvn/extensions.xml:**

```
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.1.0 https://maven.apache.org/xsd/core-extensions-1.1.0.xsd">
  <extension>
    <groupId>com.gradle</groupId>
    <artifactId>develocity-maven-extension</artifactId>
    <version>2.4.0</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 Develocity to be available to all your projects out of the box.

The extension is configured through one or more `develocity.xml` files and the `pluginManagement` sections of your `pom.xml` files. Similarly to the `extensions.xml` file, the `develocity.xml` file can be placed in your Maven project’s `.mvn` directory. Unless you intend to publish a Build Scan to [Develocity at gradle.com](https://gradle.com/scans/gradle/), the minimum configuration required is the Develocity server URL.

**Add the following to .mvn/develocity.xml:**

```
<develocity>
  <server>
    <url>https://develocity.example.com</url>
  </server>
</develocity>
```

Other mechanisms for configuring Develocity are available, including alternate file locations for `develocity.xml` and programmatic configuration with a custom Maven extension. For the full reference of the extension’s configuration, see the [configuration reference](#develocity_xml). All available configuration options will be introduced over the coming sections.

> [!NOTE]
> The Develocity Maven extension captures an identifier used to uniquely represent a given workspace that’s stored under .mvn/.develocity/develocity-workspace-id. The .mvn/.develocity folder should NOT be committed under version control.

> [!NOTE]
> The Develocity Maven extension cannot be configured using the element introduced for extensions in Maven 4 (see https://maven.apache.org/xsd/maven-4.1.0.xsd).

<a id="authenticating"></a>

### Authenticating With Develocity

Develocity 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.

> [!WARNING]
> Develocity access keys should be treated with the same secrecy as passwords. They’re used to authorize access to Develocity from a build.

<a id="automated-access-key-provisioning"></a>

#### Automated Access Key Provisioning

The easiest way to configure a build environment to authenticate with Develocity is to use the following goal:

**See Running Goals Contributed by the Develocity Maven Extension:**

```
mvn develocity: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 sign in to Develocity in your browser first if you aren’t already signed in.

When confirmed, a new access key will be generated and stored in the `.develocity/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 won’t be revoked at the server for use elsewhere. To revoke old access keys, sign in to Develocity and access “My settings” via the user menu at the top right of the page.

> [!NOTE]
> 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.

<a id="manual-access-key-configuration"></a>

#### Manual Access Key Configuration

Access keys can also be configured manually for an environment, when automated provisioning isn’t suitable.

<a id="creating-access-keys"></a>

##### Creating Access Keys

To create a new access key, sign in to Develocity 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.

> [!NOTE]
> Develocity server 2025.1+ supports access key expiration.

> [!WARNING]
> After upgrading to Develocity server 2025.1+ all existing access keys will be updated to have an expiration date.

The access key value should then be copied and configured in your build environment via file or via environment variable.

<a id="via-file"></a>

##### Via File

Develocity access keys are stored inside the Maven user home directory (`~/.m2` by default), at `.develocity/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.

```properties
develocity.example.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq
```

The file may contain multiple entries. The first entry for a given host value will be used.

<a id="via-environment-variable"></a>

##### Via Environment Variable

The access key may also be specified via the `DEVELOCITY_ACCESS_KEY` environment variable. This is typically more suitable for CI build environments.

The environment variable value format is `«server host name»=«access key»`.

```shell
export DEVELOCITY_ACCESS_KEY=develocity.example.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. In the rare case that you require access keys for multiple servers, you can specify multiple entries separated by semicolons.

```shell
export DEVELOCITY_ACCESS_KEY=develocity1.example.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq;develocity2.example.com=9y4agfiubqqjea4vonghohvuyra5bnvszop4asbqee3m3sm67w5k && \
  mvn package
```

<a id="via-configuration"></a>

##### Via Configuration

The `accessKey` can also be set via the extension’s [configuration](#configuration_reference).

<a id="tabs-1"></a>

*   <a id="tabs-1-xml"></a>
    
    XML
    
*   <a id="tabs-1-java"></a>
    
    Java
    
*   <a id="tabs-1-shell"></a>
    
    Shell
    

<a id="tabs-1-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <server>
    <url>https://develocity.example.com</url>
    <accessKey>7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq</accessKey>
  </server>
</develocity>
```

<a id="tabs-1-java--panel"></a>

**Using the Programmatic Configuration:**

```
develocity.setServer("https://develocity.example.com");
develocity.setAccessKey("7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq");
```

<a id="tabs-1-shell--panel"></a>

**The access key cannot be set via command-line argument.:**

```
The access key cannot be set via command-line argument.
```

An access key configured for the server this way will take precedence over an access key set via the environment variable or access key file.

<a id="short-lived-access-tokens"></a>

#### Short-Lived Access Tokens

Develocity access keys are long-lived, creating risks if they’re leaked. To avoid this, users can use short-lived access tokens to authenticate with Develocity. Access tokens can be used wherever an access key would be used. Access tokens are only valid for the Develocity instance that created them.

> [!NOTE]
> Develocity server version 2024.1+ supports access tokens.

> [!WARNING]
> Changing a Develocity instance’s hostname will cause all existing access tokens to become invalid.

To create an access token:

1.  Get an access key or access token for the user you want to create a token for.
    
2.  Decide which permissions the new access token should have.
    
3.  If project-level access control is enabled, decide which projects the new access token should be able to access.
    
4.  Decide how long the new access token should live.
    
5.  Make a POST request to `/api/auth/token`, optionally with the following parameters. The response will be the access token.
    
    1.  A `permissions=` query parameter with the [config values](https://docs.gradle.com/develocity/2026.1/administration/access-control/permissions-and-roles/#permissions) of each permission you want to grant. By default, all permissions for the credential used to authenticate the token request are granted to the created token.
        
    2.  If project-level access control is enabled, a `projectIds=` query parameter with the ID of each project you want to grant access to. By default, all projects for the credential used to authenticate the token request are granted to the created token.
        
    3.  An `expiresInHours=` query parameter with the token’s intended lifetime in hours, with a maximum of 24. The default is two hours, or the remaining lifetime of the credential used to authenticate the request, whichever is smaller.
        
    

The requested permissions and project ids can be specified as comma-separated lists or repeated parameters. For example, `?projectIds=a,b&projectIds=c` is valid and will request projects `a`, `b`, and `c`.

> [!NOTE]
> If project-level access control isn’t enabled, all access tokens will be granted the “Access all data without an associated project” permission even if it’s not explicitly requested.

If the user creating the token doesn’t have one of the requested permissions or projects, Develocity will respond with a _403 Forbidden_ error. If an access token is used to authenticate the creation request, its permissions and projects will be used for this check instead of the user’s. The request will also error if the requested lifetime would cause the new access token to expire after the one used to authenticate the request. Together, this means you cannot create an access token with more access or a later expiration than the credentials used to authenticate the request.

> [!NOTE]
> See the [API documentation](https://docs.gradle.com/develocity/2026.1/reference/develocity-api/) for more details on the `/api/auth/token` endpoint.

Here is an example using CURL to create an access token:

```shell
curl -X POST https://develocity.example.com/api/auth/token?permissions=publishScan,writeCache,accessDataWithoutAssociatedProject&projectIds=project-a,project-b&expiresInHours=1 \
  -H "Authorization: Bearer 7asejatf24zun43yshqufp7qi4ovcefxpykbwzqbzilcpwzb52ja"

eyJraWQiOiJ0ZXN0LWtleSIsImFsZyI6IlJTMjU2IiwidHlwIjoiSldUIn0.eyJpc19hbm9ueW1vdXMiOmZhbHNlLCJwZXJtaXNzaW9ucyI6WyJSRUFEX1ZFUlNJT04iLCJFWFBPUlRfREFUQSIsIkFDQ0VTU19EQVRBX1dJVEhPVVRfQVNTT0NJQVRFRF9QUk9KRUNUIl0sInByb2plY3RzIjp7ImEiOjEsImIiOjJ9LCJ1c2VyX2lkIjoic29tZS1pZCIsInVzZXJuYW1lIjoidGVzdCIsImZpcnN0X25hbWUiOiJhIiwibGFzdF9uYW1lIjoidXNlciIsImVtYWlsIjoiYkBncmFkbGUuY29tIiwic3ViIjoidGVzdCIsImV4cCI6NzIwMCwibmJmIjowLCJpYXQiOjAsImF1ZCI6ImV4YW1wbGUuZ3JhZGxlLmNvbSIsImlzcyI6ImV4YW1wbGUuZ3JhZGxlLmNvbSIsInRva2VuX3R5cGUiOiJhY2Nlc3NfdG9rZW4ifQ.H1_NEG1xuleP-WIAY_uvSmdd2o7i_-Ko3qhlo04zvCgrElJe7_F5jNuqsyDfnb5hvKlOe5UKG_7QPTgY9-3pFQ
```

The resulting token would have the following permissions:

*   “Publish Build Scans”
    
*   “Read and write Build Cache data”
    
*   “Access all data without an associated project”
    

And it would have access to these projects:

*   “project-a”
    
*   “project-b”
    

The token would only be usable for one hour.

<a id="build_scans"></a>

## Using Build Scans

Build Scans are a record of what happened during a build, captured and visualized by Develocity.

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.

<a id="img-build-scans"></a>

![Build Scans Can Be Published to Develocity or Develocity at gradle.com](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/build-scan-service-overview.svg)

Build Scans Can Be Published to Develocity or Develocity at gradle.com

[Develocity](https://gradle.com/develocity) is a commercial product for companies that can be hosted on their own systems and includes a Build Scan server and a Build Cache backend implementation. [Develocity at gradle.com](https://gradle.com/scans/gradle/) is a Build Scan server available for free, hosted by Gradle Inc.

There are two aspects to working with Build Scans:

*   Data collection
    
*   Publishing
    

<a id="enabling-publication-of-build-scans"></a>

### Enabling Publication of Build Scans

Enabling publication of Build Scans depends on whether you are publishing to a Develocity instance or [Develocity at gradle.com](https://gradle.com/scans/gradle/). In the case of Develocity, you need to specify the server’s location. In the case of [Develocity at gradle.com](https://gradle.com/scans/gradle/), you need to accept the terms of use.

<a id="set_the_location_of_your_develocity_instance"></a>

#### Set the Location of Your Develocity Instance

When you publish Build Scans to a Develocity instance, you must configure the location of the Develocity server.

<a id="tabs-2"></a>

*   <a id="tabs-2-xml"></a>
    
    XML
    
*   <a id="tabs-2-java"></a>
    
    Java
    
*   <a id="tabs-2-shell"></a>
    
    Shell
    

<a id="tabs-2-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <server>
    <url>https://develocity.example.com</url>
  </server>
</develocity>
```

<a id="tabs-2-java--panel"></a>

**Using the Programmatic Configuration:**

```
develocity.setServer("https://develocity.example.com");
```

<a id="tabs-2-shell--panel"></a>

```shell
mvn package -Ddevelocity.url=https://develocity.example.com
```

The precise URL you need depends on the hostname that your Develocity 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 Develocity over HTTPS. The ideal solution is for someone to add a valid SSL certificate to the Develocity instance, but we recognize that you may not be able to do that. In this case, set the `allowUntrustedServer` option to `true`:

<a id="tabs-3"></a>

*   <a id="tabs-3-xml"></a>
    
    XML
    
*   <a id="tabs-3-java"></a>
    
    Java
    
*   <a id="tabs-3-shell"></a>
    
    Shell
    

<a id="tabs-3-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <server>
    <allowUntrusted>true</allowUntrusted>
  </server>
</develocity>
```

<a id="tabs-3-java--panel"></a>

**Using the Programmatic Configuration:**

```
develocity.setAllowUntrustedServer(true);
```

<a id="tabs-3-shell--panel"></a>

```shell
mvn package -Ddevelocity.allowUntrustedServer=true
```

> [!WARNING]
> This is a convenient workaround during the initial evaluation, but it’s a serious security issue and shouldn’t be used in production.

<a id="accept-the-scans-gradle-com-terms-of-use"></a>

#### Accept the Terms of Use at Develocity at gradle.com

In order to publish to [Develocity at gradle.com](https://gradle.com/scans/gradle/), you need to accept the terms of use.

<a id="tabs-4"></a>

*   <a id="tabs-4-xml"></a>
    
    XML
    
*   <a id="tabs-4-java"></a>
    
    Java
    
*   <a id="tabs-4-shell"></a>
    
    Shell
    

<a id="tabs-4-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <termsOfUse>
      <url>https://gradle.com/help/legal-terms-of-use</url>
      <accept>true</accept>
    </termsOfUse>
  </buildScan>
</develocity>
```

<a id="tabs-4-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.setTermsOfUseUrl("https://gradle.com/help/legal-terms-of-use");
buildScan.setTermsOfUseAgree("true");
```

<a id="tabs-4-shell--panel"></a>

```shell
mvn package -Ddevelocity.scan.termsOfUse.url=https://gradle.com/help/legal-terms-of-use -Ddevelocity.scan.termsOfUse.accept=true
```

Be sure to check the terms of use at the URL shown in the above fragment.

Once you have accepted the terms of use, you can start publishing Build Scans to [Develocity at gradle.com](https://gradle.com/scans/gradle/).

> [!NOTE]
> If you don’t accept the terms of use, as explained above, you will be prompted to agree to the terms of use on the command line, before any attempt of publishing a Build Scan. This can be useful if you share your develocity.xml file with others, and you want everyone to explicitly accept those terms of use.

<a id="integrating-your-ci-tool"></a>

### Integrating Your CI Tool

<a id="jenkins"></a>

#### Jenkins

The [Gradle Jenkins plugin](https://plugins.jenkins.io/gradle) prominently displays links to the Build Scan for any Gradle or Maven build that produce Build Scans, which makes it convenient to access the Build Scans. See the following screenshot for an example:

![Gradle Jenkins Plugin](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/ci-tools/jenkins.png)

Gradle Jenkins Plugin

Besides that, the plugin allows you to instrument all CI jobs and to publish a Build Scan without modifying the underlying projects. This eases the adoption of Build Scans within the company and allows configuring the Develocity Gradle plugin or Develocity Maven extension in a central place. For more information please read the documentation [here](https://github.com/jenkinsci/gradle-plugin#develocity-integration).

<a id="teamcity"></a>

#### TeamCity

The [Develocity integration plugin](https://plugins.jetbrains.com/plugin/27005-develocity-integration) prominently displays links to the Build Scan for any Gradle or Maven build that produce Build Scans, which makes it convenient to access the Build Scans. See the following screenshot for an example:

![Develocity Build Scan Integration for TeamCity](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/ci-tools/teamcity.png)

Develocity Build Scan Integration for TeamCity

Besides that, the plugin allows you to instrument all CI jobs and to publish a Build Scan without modifying the underlying projects. This eases the adoption of Build Scans within the company and allows configuring the Develocity Gradle plugin or Develocity Maven extension in a central place.

<a id="bamboo"></a>

#### Bamboo

The [Develocity Bamboo plugin](https://marketplace.atlassian.com/apps/1230500/develocity-plugin-for-bamboo) prominently displays links to the Build Scan for any Gradle or Maven build that produce Build Scans, which makes it convenient to access the Build Scans. See the following screenshot for an example:

![Develocity Bamboo Plugin](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/ci-tools/bamboo.png)

Develocity Bamboo Plugin

Besides that, the plugin allows you to instrument all CI jobs and to publish a Build Scan without modifying the underlying projects. This eases the adoption of Build Scans within the company and allows configuring the Develocity Gradle plugin or Develocity Maven extension in a central place. For more information please read the documentation [here](https://github.com/gradle/gradle-enterprise-bamboo-plugin#gradle-auto-instrumentation).

<a id="gitlab-templates"></a>

#### GitLab Templates

The [Develocity GitLab templates](https://github.com/gradle/develocity-gitlab-templates) allow you to instrument CI jobs and to publish a Build Scan without modifying the project’s build scripts. This eases the adoption of Build Scans within the company and allows configuring the Develocity Gradle plugin or Develocity Maven extension at the CI level. For more information please read the documentation [here](https://github.com/gradle/develocity-gitlab-templates).

<a id="ide_integration"></a>

### Integration With IntelliJ IDEs

The [Develocity IntelliJ plugin](https://docs.gradle.com/develocity/intellij-plugin/1.2/) seamlessly integrates Develocity and Build Scan capabilities directly into your IDE. It displays a live timeline of goal execution and resource usage within your IntelliJ IDE.

![Live Build Timeline](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../../../intellij-plugin/1.2/_images/intellij-timeline-maven.gif)

Live Build Timeline

<a id="controlling_when_build_scans_are_published"></a>

### 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 Develocity Maven extension has several options that allow you to use whatever approach works best.

<a id="publishing-every-build-run"></a>

#### 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:

<a id="tabs-5"></a>

*   <a id="tabs-5-xml"></a>
    
    XML
    
*   <a id="tabs-5-java"></a>
    
    Java
    
*   <a id="tabs-5-shell"></a>
    
    Shell
    

<a id="tabs-5-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <publishing>
      <onlyIf>true</onlyIf>
    </publishing>
  </buildScan>
</develocity>
```

<a id="tabs-5-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.publishing(p -> p.onlyIf(__ -> true));
```

<a id="tabs-5-shell--panel"></a>

Always publishing a Build Scan cannot be done via command-line argument.

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.

If you want to deactivate Build Scans for a particular build, you can pass the `-Dscan=false` system property to Maven.

<a id="publishing-on-demand"></a>

#### 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:

<a id="tabs-6"></a>

*   <a id="tabs-6-xml"></a>
    
    XML
    
*   <a id="tabs-6-java"></a>
    
    Java
    
*   <a id="tabs-6-shell"></a>
    
    Shell
    

<a id="tabs-6-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <publishing>
      <onlyIf>false</onlyIf>
    </publishing>
  </buildScan>
</develocity>
```

<a id="tabs-6-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.publishing(p -> p.onlyIf(__ -> false));
```

<a id="tabs-6-shell--panel"></a>

**Pass the scan system property to Maven:**

```
mvn clean verify -Dscan
```

You can also publish a Build Scan for the most recently run build by invoking the `build-scan-publish-previous` goal.

**No Build Scan is produced at the end of the build:**

```
mvn clean verify --offline
```

**The Build Scan from the previous invocation is published:**

```
mvn develocity:build-scan-publish-previous
```

<a id="publishing-based-on-criteria"></a>

#### 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. You may want to publish Build Scans only under the following conditions:

1.  When the build fails
    
2.  When the user running the build is authenticated with Develocity
    
3.  If the build is running on CI
    

Such scenarios are covered by configuring a custom predicate.

<a id="tabs-7"></a>

*   <a id="tabs-7-xml"></a>
    
    XML
    
*   <a id="tabs-7-java"></a>
    
    Java
    
*   <a id="tabs-7-shell"></a>
    
    Shell
    

<a id="tabs-7-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <publishing>
      <onlyIf><![CDATA[condition && !buildResult.failures.empty]]></onlyIf>
    </publishing>
  </buildScan>
</develocity>
```

<a id="tabs-7-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.publishing(p -> p.onlyIf(__ -> {
    boolean condition = true;
    return condition;
}));
// Publish a build scan if the given condition is true, regardless of whether the build succeeds or fails
buildScan.publishing(p -> p.onlyIf(pc -> !pc.getBuildResult().getFailures().isEmpty())); // Publish a build scan only when the build fails
buildScan.publishing(p -> p.onlyIf(pc -> {
    boolean condition = true;
    return condition && !pc.getBuildResult().getFailures().isEmpty();
}));
// Publish a build scan only if the condition is true and the build fails
```

<a id="tabs-7-shell--panel"></a>

Conditional publication isn’t available via command-line argument.

**Using the Programmatic Configuration:**

```
// Publish a build scan only if the user is authenticated with the Develocity server
buildScan.publishing(p -> p.onlyIf(pc -> pc.isAuthenticated()));
```

Conditional publication isn’t available via command-line argument.

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <publishing>
      <onlyIf><![CDATA[authenticated]]></onlyIf>
    </publishing>
  </buildScan>
</develocity>
```

**Using the Programmatic Configuration:**

```
buildScan.publishing(p -> p.onlyIf(__ -> System.getenv("CI") != null));
```

Conditional publication isn’t available via command-line argument.

```xml
<develocity>
  <buildScan>
    <publishing>
      <onlyIf><![CDATA[env['CI'] != null]]></onlyIf>
    </publishing>
  </buildScan>
</develocity>
```

<a id="configuring_background_uploading"></a>

### Configuring Background Uploading

By default, Build Scans are uploaded in the background after the build has finished. This allows the build to finish sooner, but can be problematic in build environments (e.g. ephemeral CI agents) that terminate as soon as the build is finished, as the upload may be terminated before it completes. Background uploading should be disabled for such environments.

> [!NOTE]
> Prior to version 1.5 of the Maven extension, Build Scans are always uploaded before the build finishes.

Background Build Scan upload can be disabled programmatically, via a system property, or via the `develocity.xml` configuration file.

<a id="tabs-8"></a>

*   <a id="tabs-8-xml"></a>
    
    XML
    
*   <a id="tabs-8-java"></a>
    
    Java
    
*   <a id="tabs-8-shell"></a>
    
    Shell
    

<a id="tabs-8-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <backgroundBuildScanUpload>false</backgroundBuildScanUpload>
  </buildScan>
</develocity>
```

<a id="tabs-8-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.setUploadInBackground(false);
```

<a id="tabs-8-shell--panel"></a>

**Add the develocity.scan.uploadInBackground system property to Maven:**

```
mvn clean verify -Ddevelocity.scan.uploadInBackground=false
```

It may be desirable to conditionally set the value based on the environment.

<a id="tabs-9"></a>

*   <a id="tabs-9-xml"></a>
    
    XML
    
*   <a id="tabs-9-java"></a>
    
    Java
    
*   <a id="tabs-9-shell"></a>
    
    Shell
    

<a id="tabs-9-xml--panel"></a>

```xml
<develocity>
  <buildScan>
    <backgroundBuildScanUpload>#{env['CI'] == null}</backgroundBuildScanUpload>
  </buildScan>
</develocity>
```

<a id="tabs-9-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.setUploadInBackground(System.getenv("CI") == null);
```

<a id="tabs-9-shell--panel"></a>

**Add the develocity.scan.uploadInBackground system property only on the CI build configuration:**

```
mvn clean verify -Ddevelocity.scan.uploadInBackground=false
```

<a id="configuring-project-identifier"></a>

### Configuring Project Identifier

The documentation at the [Project-level access control](https://docs.gradle.com/develocity/2026.1/administration/access-control/project-level-access-control/) provides detailed information regarding project-level access control.

> [!NOTE]
> Versions before 1.19 of this extension don’t allow specifying project identifier

Project identifier can be set programmatically, via a system property, or via the `develocity.xml` configuration file.

<a id="tabs-10"></a>

*   <a id="tabs-10-xml"></a>
    
    XML
    
*   <a id="tabs-10-java"></a>
    
    Java
    
*   <a id="tabs-10-shell"></a>
    
    Shell
    

<a id="tabs-10-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <projectId>myProject</projectId>
</develocity>
```

<a id="tabs-10-java--panel"></a>

**Using the Programmatic Configuration:**

```
develocity.setProjectId("myProject");
```

<a id="tabs-10-shell--panel"></a>

**Add the develocity.projectId system property to Maven:**

```
mvn clean verify -Ddevelocity.projectId=myProject
```

<a id="capturing_goal_input_files"></a>

### Capturing Goal Input File Fingerprints

Build Scans capture hashes of goal inputs, to enable identifying changes to inputs when comparing builds, among other features. The overall hash value of each goal input property enables identifying which properties changed for a goal execution (e.g. the source or the classpath for Java compilation) when comparing two builds. In addition, the paths and content hashes of individual input files of each property are captured by default. They allow identifying which _individual files_ changed for a goal execution when comparing two builds.

<a id="when-to-disable"></a>

#### When to Disable

Capturing file fingerprints 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. In such cases, capturing file fingerprints can be disabled.

However, if you are using Develocity and utilising its Build Cache to accelerate your builds, it’s strongly recommended to keep it enabled as identifying which files have changed between builds with build comparison is extremely effective for diagnosing unexpected Build Cache misses.

If you are using Develocity for [Predictive Test Selection](https://docs.gradle.com/develocity/2026.1/using-develocity/predictive-test-selection/) enabling capture of file fingerprints is a prerequisite.

<a id="how-to-disable"></a>

#### How to Disable

File fingerprint capture can be enabled/disabled programmatically, via a system property, or via the `develocity.xml` configuration file.

<a id="tabs-11"></a>

*   <a id="tabs-11-xml"></a>
    
    XML
    
*   <a id="tabs-11-java"></a>
    
    Java
    
*   <a id="tabs-11-shell"></a>
    
    Shell
    

<a id="tabs-11-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <capture>
      <fileFingerprints>false</fileFingerprints>
    </capture>
  </buildScan>
</develocity>
```

<a id="tabs-11-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.getCapture().setFileFingerprints(false);
```

<a id="tabs-11-shell--panel"></a>

**Add the develocity.scan.captureFileFingerprints system property to Maven:**

```
mvn clean verify -Ddevelocity.scan.captureFileFingerprints=false
```

<a id="capturing-build-and-test-logging"></a>

### Capturing Build and Test Logging

By default, logging generated during the build are captured and displayed in Build Scans.

Note that when disabling test logging capturing, test failures will still be captured.

<a id="when-to-disable-2"></a>

#### When to Disable

You may want to skip capturing build or test logging for security/privacy reasons (i.e., some logging may leak sensitive data), or performance/storage reasons (i.e., some goals/tests may produce a lot of logging that are irrelevant for your usage of Build Scans).

<a id="how-to-disable-2"></a>

#### How to Disable

Logging capture can be enabled/disabled programmatically, via a system property, or via the `develocity.xml` configuration file.

<a id="tabs-12"></a>

*   <a id="tabs-12-xml"></a>
    
    XML
    
*   <a id="tabs-12-java"></a>
    
    Java
    
*   <a id="tabs-12-shell"></a>
    
    Shell
    

<a id="tabs-12-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <capture>
      <buildLogging>false</buildLogging>
      <testLogging>false</testLogging>
    </capture>
  </buildScan>
</develocity>
```

<a id="tabs-12-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.getCapture().setBuildLogging(false);
buildScan.getCapture().setTestLogging(false);
```

<a id="tabs-12-shell--panel"></a>

**Add the develocity.scan.captureBuildLogging and develocity.scan.captureTestLogging system properties to Maven:**

```
mvn clean verify -Ddevelocity.scan.captureBuildLogging=false -Ddevelocity.scan.captureTestLogging=false
```

<a id="capturing-resource-usage"></a>

### Capturing Resource Usage

_(Maven extension 1.22+)_

Build Scans capture information on key resources of the machine executing the build. This includes CPU load, memory, disk usage, network activity, and the names of the most CPU-intensive processes. These insights can help analyze poor build performance, whether due to the build needing too many resources or factors external to the build. It can also help understand if the machine is underutilized and if it could do more work.

By default, resource usage is captured and displayed in Build Scans.

<a id="when-to-disable-3"></a>

#### When to Disable

You may want to skip capturing resource usage for security/privacy reasons. Note that the names of processes external to the build (i.e. not the build process nor its descendants) can be [obfuscated](#obfuscating_identifying_data).

<a id="how-to-disable-3"></a>

#### How to Disable

Resource usage capture can be enabled/disabled programmatically, via a system property, or via the `develocity.xml` configuration file.

<a id="tabs-13"></a>

*   <a id="tabs-13-xml"></a>
    
    XML
    
*   <a id="tabs-13-java"></a>
    
    Java
    
*   <a id="tabs-13-shell"></a>
    
    Shell
    

<a id="tabs-13-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <capture>
      <resourceUsage>false</resourceUsage>
    </capture>
  </buildScan>
</develocity>
```

<a id="tabs-13-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.getCapture().setResourceUsage(false);
```

<a id="tabs-13-shell--panel"></a>

**Add the develocity.scan.captureResourceUsage system property to Maven:**

```
mvn clean verify -Ddevelocity.scan.captureResourceUsage=false
```

<a id="extending_build_scans"></a>

### 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’s 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 figures 2 and 3:

<a id="img-scan-with-custom-data-1"></a>

![A Build Scan Containing Tags and Links](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/scan-with-custom-data-1.png)

A Build Scan Containing Tags and Links

<a id="img-scan-with-custom-data-2"></a>

![A Build Scan Containing Custom Values](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/scan-with-custom-data-2.png)

A Build Scan Containing Custom Values

Develocity 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 4, for example, we’re filtering for all Maven Build Scans that have the tag "CI" and a git branch name of "release":

<a id="img-filtered-list-view"></a>

![A Filtered List of Build Scans in Develocity](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/build-scan-filtered-list.png)

A Filtered List of Build Scans in Develocity

<a id="adding-tags"></a>

#### Adding Tags

Tags are typically used to indicate the type or class of a build, or a key characteristic. They’re prominent in the user interface and quickly inform a user about the nature of a build. A build can have zero or more tags.

<a id="tabs-14"></a>

*   <a id="tabs-14-xml"></a>
    
    XML
    
*   <a id="tabs-14-java"></a>
    
    Java
    
*   <a id="tabs-14-shell"></a>
    
    Shell
    

<a id="tabs-14-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <tags>
      <tag>my tag</tag>
    </tags>
  </buildScan>
</develocity>
```

**Add the following to a project pom.xml:**

```
<project>
  <!-- other build configuration -->
  <pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <buildScan>
            <tags>
              <tag>my tag</tag>
            </tags>
          </buildScan>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
  </pluginManagement>
</project>
```

<a id="tabs-14-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.tag("my tag");
```

<a id="tabs-14-shell--panel"></a>

**Add the scan.tag.<tag> system property to Maven:**

```
mvn package -Dscan.tag.CI
```

> [!NOTE]
> 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’re 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](#img-scan-with-custom-data-1).

> [!NOTE]
> The Develocity Maven extension imposes limits on captured tags: Maximum tag count: 50 Maximum tag length: 200 characters

<a id="adding-links"></a>

#### 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.

<a id="tabs-15"></a>

*   <a id="tabs-15-xml"></a>
    
    XML
    
*   <a id="tabs-15-java"></a>
    
    Java
    
*   <a id="tabs-15-shell"></a>
    
    Shell
    

<a id="tabs-15-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <links>
      <link>
        <name>my link</name>
        <url>http://example.com</url>
      </link>
    </links>
  </buildScan>
</develocity>
```

**Add the following to a project pom.xml:**

```
<project>
  ...
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>com.gradle</groupId>
        <artifactId>develocity-maven-extension</artifactId>
        <configuration>
          <develocity>
            <buildScan>
              <links>
                <link>
                  <name>my link</name>
                  <url>http://example.com</url>
                </link>
              </links>
            </buildScan>
          </develocity>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</project>
```

<a id="tabs-15-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.link("my link", "http://example.com");
```

<a id="tabs-15-shell--panel"></a>

**Add the scan.link.<name>=<URL> system property to Maven:**

```
mvn package -Dscan.link.VCS=https://github.com/myorg/my-super-project/tree/my-new-feature
```

> [!NOTE]
> 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](#img-scan-with-custom-data-1), which shows how a label for a given VCS (here GitHub) becomes a hyperlink that anyone viewing the Build Scan can follow.

> [!NOTE]
> The Develocity Maven extension imposes limits on captured links: Maximum link count: 20 Maximum link label length: 100 characters Maximum link URL length: 100,000 characters

<a id="adding-custom-values"></a>

#### 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.

<a id="tabs-16"></a>

*   <a id="tabs-16-xml"></a>
    
    XML
    
*   <a id="tabs-16-java"></a>
    
    Java
    
*   <a id="tabs-16-shell"></a>
    
    Shell
    

<a id="tabs-16-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildScan>
    <values>
      <value>
        <name>my name</name>
        <value>my value</value>
      </value>
    </values>
  </buildScan>
</develocity>
```

**Add the following to a project pom.xml:**

```
<project>
  ...
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>com.gradle</groupId>
        <artifactId>develocity-maven-extension</artifactId>
        <configuration>
          <develocity>
            <buildScan>
              <values>
                <value>
                  <name>Build Number</name>
                  <value>${project.buildNumber}</value>
                </value>
              </values>
            </buildScan>
          </develocity>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</project>
```

<a id="tabs-16-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.value("my name", "my value");
```

<a id="tabs-16-shell--panel"></a>

**Add the scan.value.<name>=<value> system property to Maven:**

```
mvn package -Dscan.value.CIBuildType=QA_Build
```

> [!NOTE]
> Prefer the programmatic access. If specified in parent POM, custom values will be applied to all projects inheriting from it.

As with tags, you can filter Build Scans by custom values in Develocity.

> [!NOTE]
> The Develocity Maven extension imposes limits on captured custom values: Maximum custom value count: 1,000 Maximum custom value key length: 1,000 characters Maximum custom value value length: 100,000 characters

<a id="callbacks"></a>

### Callbacks

The Build Scan API allows to programmatically interact with the Build Scan configuration of the Develocity Maven extension. Please see the [Javadoc](https://docs.gradle.com/develocity/maven/2.4/javadoc/) for the complete API documentation.

<a id="executing-operations-at-the-end-of-the-build"></a>

#### Executing Operations at the End of the Build

What if you want to execute some code based on data that’s 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:

<a id="tabs-17"></a>

*   <a id="tabs-17-xml"></a>
    
    XML
    
*   <a id="tabs-17-java"></a>
    
    Java
    
*   <a id="tabs-17-shell"></a>
    
    Shell
    

<a id="tabs-17-xml--panel"></a>

Adding data at the end of the build isn’t available via XML configuration.

<a id="tabs-17-java--panel"></a>

**Using the Programmatic Configuration:**

```
File outputDir = new File(mavenSession.getCurrentProject().getBuild().getOutputDirectory());
buildScan.buildFinished(buildResult -> {
    long size = org.apache.commons.io.FileUtils.sizeOfDirectory(outputDir);
    buildScan.value("Disk usage (target dir)", String.valueOf(size));
});
```

<a id="tabs-17-shell--panel"></a>

Adding data at the end of the build isn’t available via command-line argument.

The `buildFinished()` action has access to a `BuildResult` instance that you can use to determine whether the build failed or not, like so:

<a id="tabs-18"></a>

*   <a id="tabs-18-xml"></a>
    
    XML
    
*   <a id="tabs-18-java"></a>
    
    Java
    
*   <a id="tabs-18-shell"></a>
    
    Shell
    

<a id="tabs-18-xml--panel"></a>

Adding data at the end of the build isn’t available via XML configuration.

<a id="tabs-18-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.buildFinished(buildResult -> {
    buildResult.getFailures().forEach(failure ->
        buildScan.value("Failed with", failure.getMessage())
    );
});
```

<a id="tabs-18-shell--panel"></a>

Adding data at the end of the build isn’t available via command-line argument.

<a id="executing-operations-when-a-build-scan-is-published"></a>

#### Executing Operations When a Build Scan Is Published

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:

<a id="tabs-19"></a>

*   <a id="tabs-19-xml"></a>
    
    XML
    
*   <a id="tabs-19-java"></a>
    
    Java
    
*   <a id="tabs-19-shell"></a>
    
    Shell
    

<a id="tabs-19-xml--panel"></a>

Executing operations when a Build Scan is published cannot be configured via XML.

<a id="tabs-19-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.buildScanPublished(it -> {
    try {
        Files.write(Paths.get("buildScans.txt"), it.getBuildScanId().getBytes(), StandardOpenOption.APPEND);
    } catch (IOException e) {
        throw new UncheckedIOException("Failed to write to journal", e);
    }
});
```

<a id="tabs-19-shell--panel"></a>

Executing operations when a Build Scan is published cannot be configured via command-line argument.

<a id="executing-expensive-operations"></a>

#### Executing Expensive Operations

Some data that you may want 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:

<a id="tabs-20"></a>

*   <a id="tabs-20-xml"></a>
    
    XML
    
*   <a id="tabs-20-java"></a>
    
    Java
    
*   <a id="tabs-20-shell"></a>
    
    Shell
    

<a id="tabs-20-xml--panel"></a>

Doing expensive work in the background cannot be configured via XML.

<a id="tabs-20-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildScan.background(api -> {
    try {
        File projectDir = mavenSession.getCurrentProject().getBasedir();
        String commitId = org.eclipse.jgit.lib.ObjectId.toString(
            org.eclipse.jgit.api.Git.open(projectDir)
                .getRepository()
                .resolve("HEAD")
        );
        api.value("Git Commit ID", commitId);
    } catch (IOException e) {
        throw new UncheckedIOException("Failed to read Git Commit ID", e);
    }
});
```

<a id="tabs-20-shell--panel"></a>

Doing expensive work in the background cannot be configured via command-line argument.

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](https://docs.gradle.com/downloads/maven-extension-javadoc/2.4.0/com/gradle/develocity/agent/maven/api/scan/BuildScanApi.html#background\(java.util.function.Consumer\)) for more information.

<a id="executing-operations-only-once"></a>

#### Executing Operations Only Once

You may want to ensure that a given operation 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.

<a id="tabs-21"></a>

*   <a id="tabs-21-xml"></a>
    
    XML
    
*   <a id="tabs-21-java"></a>
    
    Java
    
*   <a id="tabs-21-shell"></a>
    
    Shell
    

<a id="tabs-21-xml--panel"></a>

Running custom Build Scan configuration logic once isn’t available via XML configuration.

<a id="tabs-21-java--panel"></a>

**Using the Programmatic Configuration:**

```
// This will be executed once
buildScan.executeOnce("capture custom data", api -> api.tag("my custom tag"));
// This will not be executed and will silently be ignored
buildScan.executeOnce("capture custom data", api -> api.tag("my other custom tag"));
// This will be executed once
buildScan.executeOnce("publish to journal", api -> api.buildScanPublished(journalServer::add));
```

<a id="tabs-21-shell--panel"></a>

Running custom Build Scan configuration logic once isn’t available via command-line argument.

<a id="obfuscating_identifying_data"></a>

#### Obfuscating Identifying Data

Build Scans capture certain identifying information such as the operating system username, hostname, network addresses, and CPU-intensive process names. You may choose to obfuscate this data so that it isn’t decipherable in Build Scans when viewed. To do this, you can use the `BuildScanApi#obfuscation()` method.

With Maven extension v1.6.3+, you can register obfuscation values on the [develocity.xml](#develocity_xml). These will always be applied, even in case of very early build failures, that would prevent programmatic configuration via the BuildScan API. If the BuildScan API gets eventually called, obfuscation functions registered with it will have precedence over the values defined via XML.

The following examples show registering obfuscation functions for the different identifying data.

<a id="tabs-22"></a>

*   <a id="tabs-22-xml"></a>
    
    XML
    
*   <a id="tabs-22-java"></a>
    
    Java
    
*   <a id="tabs-22-shell"></a>
    
    Shell
    

<a id="tabs-22-xml--panel"></a>

**Obfuscating the username via develocity.xml:**

```
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use a redacted value. -->
      <username>obfuscated</username>
    </obfuscation>
  </buildScan>
</develocity>
---
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use Spring Expression Language. The 'username' variable can be used to get access to the
           username that would be captured. -->
      <username>{username.substring(0,1)}</username>
    </obfuscation>
  </buildScan>
</develocity>
---
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use Spring Expression Language. The 'sha512' method can be used. -->
      <username>{sha512(username)}</username>
    </obfuscation>
  </buildScan>
</develocity>
```

<a id="tabs-22-java--panel"></a>

**Obfuscating the username using the Programmatic Configuration:**

```
buildScan.obfuscation(obfuscation -> obfuscation.username(s -> s.chars()
    .mapToObj(c -> String.valueOf(Character.getNumericValue(c)))
    .collect(java.util.stream.Collectors.joining())
));
```

<a id="tabs-22-shell--panel"></a>

Obfuscating the username cannot be configured via command-line argument.

<a id="tabs-23"></a>

*   <a id="tabs-23-xml"></a>
    
    XML
    
*   <a id="tabs-23-java"></a>
    
    Java
    
*   <a id="tabs-23-shell"></a>
    
    Shell
    

<a id="tabs-23-xml--panel"></a>

**Obfuscating the hostnames via develocity.xml:**

```
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use a redacted value. -->
      <hostname>obfuscated</hostname>
    </obfuscation>
  </buildScan>
</develocity>
---
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use Spring Expression Language. -->
      <hostname>#{isTrue(env['CI']) ? 'CI agent' : 'Local agent'}</hostname>
    </obfuscation>
  </buildScan>
</develocity>
```

<a id="tabs-23-java--panel"></a>

**Obfuscating the hostnames using the Programmatic Configuration:**

```
buildScan.obfuscation(obfuscation -> obfuscation.hostname(s -> s.chars()
    .mapToObj(c -> String.valueOf(Character.getNumericValue(c)))
    .collect(java.util.stream.Collectors.joining())
));
```

<a id="tabs-23-shell--panel"></a>

Obfuscating the hostnames cannot be configured via command-line argument.

<a id="tabs-24"></a>

*   <a id="tabs-24-xml"></a>
    
    XML
    
*   <a id="tabs-24-java"></a>
    
    Java
    
*   <a id="tabs-24-shell"></a>
    
    Shell
    

<a id="tabs-24-xml--panel"></a>

**Obfuscating the IP addresses via develocity.xml:**

```
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use a redacted value.-->
      <ipAddresses>#{{'0.0.0.0'}}</ipAddresses>
    </obfuscation>
  </buildScan>
</develocity>
---
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use Spring Expression Language. The 'sha512' method can be used.
           The 'ipAddresses' variable can be used to get access to the list of IP addresses
           that would be captured. -->
      <ipAddresses>#{sha512(ipAddresses)}</ipAddresses>
    </obfuscation>
  </buildScan>
</develocity>
```

<a id="tabs-24-java--panel"></a>

**Obfuscating the IP addresses using the Programmatic Configuration:**

```
buildScan.obfuscation(obfuscation -> obfuscation.ipAddresses(addresses -> addresses.stream()
    .map(address -> "0.0.0.0")
    .collect(java.util.stream.Collectors.toList())
));
```

<a id="tabs-24-shell--panel"></a>

Obfuscating the IP addresses cannot be configured via command-line argument.

<a id="tabs-25"></a>

*   <a id="tabs-25-xml"></a>
    
    XML
    
*   <a id="tabs-25-java"></a>
    
    Java
    
*   <a id="tabs-25-shell"></a>
    
    Shell
    

<a id="tabs-25-xml--panel"></a>

**Obfuscating the non-build-related process names via develocity.xml (Maven extension 1.22+):**

```
<develocity>
  <buildScan>
    <obfuscation>
      <!-- Use a redacted value. -->
      <externalProcessName>obfuscated</externalProcessName>
    </obfuscation>
  </buildScan>
</develocity>
```

<a id="tabs-25-java--panel"></a>

**Obfuscating the non-build-related process names using the Programmatic Configuration (Maven extension 1.22+):**

```
buildScan.obfuscation(obfuscation -> obfuscation.externalProcessName(s -> s.chars()
    .mapToObj(c -> String.valueOf(Character.getNumericValue(c)))
    .collect(java.util.stream.Collectors.joining())
));
```

<a id="tabs-25-shell--panel"></a>

Obfuscating the non-build-related process names cannot be configured via command-line argument.

See the [BuildScanApi#obfuscation() API reference](https://docs.gradle.com/downloads/maven-extension-javadoc/2.4.0/com/gradle/develocity/agent/maven/api/scan/BuildScanApi.html#obfuscation\(java.util.function.Consumer\)) for more information.

<a id="viewing_test_results"></a>

## Viewing Test Results

The Develocity extension will capture test execution results and publish them to the Develocity server. After your Build Scan® is ready, you can view and analyze the results.

Any test framework that’s based on JUnit Platform should work with Develocity without requiring any change. Contact us at [support.gradle.com](https://support.gradle.com) if the results aren’t correctly displayed for the test framework that you are using.

<a id="the-test-overview-page"></a>

### The Test Overview Page

The Test overview page shows a high level overview of all the tests that were executed as part of the build. The test results are displayed hierarchically: they’re grouped by _goal_, _test suite_ and finally _test case_. Next to each _goal_, _test suite_ and _test case_, the outcome and the total time are displayed.

![Tests Overview Dashboard](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/test-overview.png)

Tests Overview Dashboard

<a id="test-outcomes"></a>

#### Test Outcomes

Every _goal_, _test suite_ and _test case_ has an outcome. For a _goal_, the outcome is either `SUCCESS`, indicating that the _goal_ was successful, `FLAKY`, which means that at least one _test suite_ was flaky, or `FAILED`, if a test suite has failed.

For _test suites_, the outcome can be `SUCCESS`, meaning that all executed _test cases_ were successful, `FAILED`, indicating that at least one _test case_ failed, `FLAKY`, meaning that at least one _test case_ was successful only after retry, or `SKIPPED`, meaning that the _test suite_ wasn’t executed.

For _test cases_, the outcome can be `SKIPPED`, meaning that the _test case_ wasn’t executed, `SUCCESS`, meaning that the _test case_ was successful, `FAILED` indicating that the _test case_ was failed, or `FLAKY`, meaning that the test case was successful only after retry.

<a id="test-timings"></a>

#### Test Timings

On a _goal_, the _total time_ corresponds to the total amount of wall clock time the goal took to execute.

On a test suite, the _total time_ corresponds to the total amount of wall clock time the test suite took to execute.

On a test case, the _total time_ corresponds to the total amount of wall clock time the test case took to execute. Clicking on a _test goal_ will open the details for this _test goal_, which will display the results of the _test suites_ which ran as part of this _test goal_.

<a id="test-goal-details-page"></a>

### Test Goal Details Page

The Test goal details page displays the results of the _test suites_ which ran as part of the selected _test goal_.

![.Example of a Test Goal Details Page](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/test-goal-details.png)

Example of a Test Goal Details Page

At the top of the page, it also shows relevant configuration values, and performance statistics. Clicking on a _test suite_ will open the details for this _test suite_, which will display the results of the _test cases_ which ran as part of this _test suite_.

<a id="test-suite-details-page"></a>

### Test Suite Details Page

The Test suite details page displays the results of the _test cases_ which ran as part of the selected _test suite_.

![.Example of a Test Suite Details Page](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/test-suite-details.png)

Example of a Test Suite Details Page

Errors that may have happened during the setup or cleanup phase of the _test suite_ execution are reported in this page.

The suite’s overall outcome is broken down into _Class setup/cleanup_ and _Test execution_ outcomes. _Class setup/cleanup_ tracks whether a failure occurred in the suite’s setup / teardown callbacks, and _Test execution_ whether it was a _test case_ that caused the _test suite_ to fail.

At the bottom of the page, the _test cases_ that were executed as part of this _test suite_ are listed, along with the time and outcome of each execution. A _test case_ may be run more than once in case of retries.

Clicking on a _test case_ will open the details for this _test case_, which will display the results of the executions of this _test case_.

<a id="test-case-details-page"></a>

### Test Case Details Page

The Test case details page displays the results of the executions of the selected _test case_.

![.Example of a Test Case Details Page](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/test-case-details.png)

Example of a Test Case Details Page

The total time and outcome of each execution of the selected _test case_ are displayed on the page. If an execution failed with an exception, then the exception is displayed under the corresponding execution.

<a id="troubleshooting"></a>

### Troubleshooting

<a id="failed-background-build-scan-uploads"></a>

#### Failed Background Build Scan Uploads

When using background Build Scan uploading (default behaviour since Maven extension version 1.5, see [this section](#configuring_background_uploading) for configuration options) upload failures aren’t visible in the build logging due to occurring in a background process after the build has finished. Instead, errors are logged to a file located at `~/.m2/.develocity/build-scan-data/upload-failure.log`. If this additional information doesn’t help to resolve the failure, please contact technical support and include the contents of this log file.

If the background upload process fails to start, a warning is shown in the build console and uploading is performed in the build process. If this occurs, please contact technical support with the log files located at `~/.m2/.develocity/build-scan-data/<<extension-version>>/pending-uploads/*.log`.

<a id="slow-resolution-of-host-name"></a>

#### Slow Resolution of Host Name

Build Scans attempt to determine the host name of the machine. An [issue affecting macOS](https://bugs.openjdk.org/browse/JDK-7180557) 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:

**/etc/hosts:**

```
127.0.0.1   localhost mbpro.local
::1         localhost mbpro.local
```

<a id="forked-maven-builds"></a>

#### Forked Maven Builds

Several Maven plugins fork a new Maven build during the execution of a build. When the project is configured to publish a Build Scan, the forked Maven build will publish one as well. Depending on the use case, this behavior is desirable or not. The Develocity Maven extension doesn’t detect if it’s executed within a forked Maven build to avoid publishing a Build Scan. In some circumstances and specific setups it can lead to errors on the forked Maven build and we then advise you to disable the Build Scan functionality of the Develocity Maven extension for such builds. This doesn’t affect the use of the Build Cache.

The following sections show how to disable publishing a Build Scan for various Maven plugins that spawn forked Maven builds.

<a id="maven-invoker-plugin"></a>

##### Maven Invoker Plugin

For the [Maven invoker plugin](https://maven.apache.org/plugins/maven-invoker-plugin/) set the `develocity.scan.disabled` system property to `true`.

**pom.xml:**

```
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-invoker-plugin</artifactId>
    <configuration>
        ...
        <properties>
            <develocity.scan.disabled>true</develocity.scan.disabled>
        </properties>
        ...
    </configuration>
    ...
</plugin>
```

<a id="maven-verifier"></a>

##### Maven Verifier

For the [Maven verifier](https://maven.apache.org/shared/maven-verifier/#) you can set the `develocity.scan.disabled` CLI option on the `Verifier` to `true`.

```java
verifier.addCliOption("-Ddevelocity.scan.disabled=true");
```

<a id="maven-archetype-plugin"></a>

##### Maven Archetype Plugin

While integration testing an archetype with the [Maven archetype plugin](https://maven.apache.org/archetype/maven-archetype-plugin/integration-test-mojo.html) a Maven build will be forked. To disable Build Scan publishing, you can set the `develocity.scan.disabled` system property to `true`.

**pom.xml:**

```
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-archetype-plugin</artifactId>
    <configuration>
        ...
        <properties>
            <develocity.scan.disabled>true</develocity.scan.disabled>
        </properties>
        ...
    </configuration>
    ...
</plugin>
```

<a id="maven-release-plugin"></a>

##### Maven Release Plugin

For the [Maven release plugin](https://maven.apache.org/maven-release/maven-release-plugin/) set the `develocity.scan.disabled` system property to `true`.

**pom.xml:**

```
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-release-plugin</artifactId>
    <configuration>
        ...
        <arguments>
            -Ddevelocity.scan.disabled=true
        </arguments>
        ...
    </configuration>
    ...
</plugin>
```

<a id="using-the-build-cache"></a>

## Using the Build Cache

The Build Cache speeds up your builds by reusing outputs from any previous build, on any machine that’s 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 Build 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 Develocity provides. This remote Build Cache allows you to share cached outputs across your whole team, including local and CI builds.

Refer to the [Build Cache guide](https://docs.gradle.com/develocity/maven/2.4/build-cache-maven/) 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.

> [!NOTE]
> The build caching functionality for Maven requires a Develocity license. The free Develocity at gradle.com server doesn’t allow using the Build Cache.

<a id="configuring-the-build-cache"></a>

### Configuring the Build Cache

In order to use build caching for Apache Maven, you need to configure the location of your Develocity server.

<a id="tabs-26"></a>

*   <a id="tabs-26-xml"></a>
    
    XML
    
*   <a id="tabs-26-java"></a>
    
    Java
    
*   <a id="tabs-26-shell"></a>
    
    Shell
    

<a id="tabs-26-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <server>
    <url>https://develocity.example.com</url>
  </server>
</develocity>
```

<a id="tabs-26-java--panel"></a>

**Using the Programmatic Configuration:**

```
develocity.setServer("https://develocity.example.com");
```

<a id="tabs-26-shell--panel"></a>

```shell
mvn package -Ddevelocity.url=https://develocity.example.com
```

The precise URL you need depends on the hostname that your Develocity 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 Develocity over HTTPS. The ideal solution is for someone to add a valid SSL certificate to the Develocity instance, but we recognize that you may not be able to do that. In this case, set the `allowUntrustedServer` option to `true`:

<a id="tabs-27"></a>

*   <a id="tabs-27-xml"></a>
    
    XML
    
*   <a id="tabs-27-java"></a>
    
    Java
    
*   <a id="tabs-27-shell"></a>
    
    Shell
    

<a id="tabs-27-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <server>
    <allowUntrusted>true</allowUntrusted>
  </server>
</develocity>
```

<a id="tabs-27-java--panel"></a>

**Using the Programmatic Configuration:**

```
develocity.setAllowUntrustedServer(true);
```

<a id="tabs-27-shell--panel"></a>

```shell
mvn package -Ddevelocity.allowUntrustedServer=true
```

> [!WARNING]
> This is a convenient workaround during the initial evaluation, but it’s a serious security issue and shouldn’t be used in production.

<a id="configuring_the_local_cache"></a>

#### Configuring the Local Build Cache

The extension uses a local Build Cache to store build outputs in the local filesystem. It prevents network round-trips by storing both outputs that local builds created and outputs that were downloaded from the remote Build Cache.

<a id="disabling_the_local_cache"></a>

##### Disabling the Local Build Cache

The local Build Cache is enabled by default. This can be changed by setting the `enabled` option to `false`.

<a id="tabs-28"></a>

*   <a id="tabs-28-xml"></a>
    
    XML
    
*   <a id="tabs-28-java"></a>
    
    Java
    
*   <a id="tabs-28-shell"></a>
    
    Shell
    

<a id="tabs-28-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <local>
      <enabled>false</enabled>
    </local>
  </buildCache>
</develocity>
```

<a id="tabs-28-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getLocal().setEnabled(false);
```

<a id="tabs-28-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.local.enabled=false
```

<a id="disabling_local_store"></a>

##### Disabling Local Store

By default, outputs are stored in the local Build Cache if it’s enabled and the build includes the `clean` lifecycle phase. Storing outputs in the local Build Cache can be disabled explicitly by setting the `storeEnabled` option to `false`.

<a id="tabs-29"></a>

*   <a id="tabs-29-xml"></a>
    
    XML
    
*   <a id="tabs-29-java"></a>
    
    Java
    
*   <a id="tabs-29-shell"></a>
    
    Shell
    

<a id="tabs-29-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <local>
      <storeEnabled>false</storeEnabled>
    </local>
  </buildCache>
</develocity>
```

<a id="tabs-29-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getLocal().setStoreEnabled(false);
```

<a id="tabs-29-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.local.storeEnabled=false
```

<a id="changing_the_local_cache_directory"></a>

##### Changing the Local Build Cache Directory

The local Build Cache is located at `${user.home}/.m2/.develocity/build-cache` by default. This can be changed by setting the `directory` option.

<a id="tabs-30"></a>

*   <a id="tabs-30-xml"></a>
    
    XML
    
*   <a id="tabs-30-java"></a>
    
    Java
    
*   <a id="tabs-30-shell"></a>
    
    Shell
    

<a id="tabs-30-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <local>
      <directory>/path/to/local/build-cache</directory>
    </local>
  </buildCache>
</develocity>
```

<a id="tabs-30-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getLocal().setDirectory(new File("path/to/local/build-cache"));
```

<a id="tabs-30-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.local.directory=path/to/local/build-cache
```

> [!NOTE]
> It’s 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 round-trips, you should explicitly configure the local Build Cache directory to a path on the local filesystem.

<a id="Configuring_local_cache_cleanup"></a>

##### Configuring Local Build Cache Cleanup

To prevent the local Build Cache from growing in size indefinitely, the local Build 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`.

<a id="tabs-31"></a>

*   <a id="tabs-31-xml"></a>
    
    XML
    
*   <a id="tabs-31-java"></a>
    
    Java
    
*   <a id="tabs-31-shell"></a>
    
    Shell
    

<a id="tabs-31-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <local>
      <cleanup>
        <enabled>false</enabled>
      </cleanup>
    </local>
  </buildCache>
</develocity>
```

<a id="tabs-31-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getLocal().getCleanupPolicy().setEnabled(false);
```

<a id="tabs-31-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.local.cleanup.enabled=false
```

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](https://en.wikipedia.org/wiki/ISO_8601#Durations) `PnDTnHnMn.nS`.

<a id="tabs-32"></a>

*   <a id="tabs-32-xml"></a>
    
    XML
    
*   <a id="tabs-32-java"></a>
    
    Java
    
*   <a id="tabs-32-shell"></a>
    
    Shell
    

<a id="tabs-32-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <local>
      <cleanup>
        <retention>P30D</retention>
        <interval>P10D</interval>
      </cleanup>
    </local>
  </buildCache>
</develocity>
```

<a id="tabs-32-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getLocal().getCleanupPolicy().setRetentionPeriod(java.time.Duration.ofDays(30));
buildCache.getLocal().getCleanupPolicy().setCleanupInterval(java.time.Duration.ofDays(10));
```

<a id="tabs-32-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.local.cleanup.retention=P30D -Ddevelocity.cache.local.cleanup.interval=P10D
```

<a id="working-offline"></a>

##### 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 Develocity server allows build caching for Maven. The result of this check is stored in a token in the [user home](#anatomy-of-the-develocity-directory). As long as you are working online, the token is refreshed every hour. The local Build Cache will keep working in offline mode until that token expires after 24 hours.

<a id="configuring_the_remote_cache"></a>

#### Configuring the Remote Build Cache

Develocity provides a Build Cache node that’s built into the server. Additionally, remote Build Cache nodes can be spun up and connected to the server. By default, the built-in Build Cache node of the Develocity server is used.

<a id="using_a_different_cache_node"></a>

##### Using a Different Build Cache Node

The address of the remote Build Cache node can be configured in the `server` option.

<a id="tabs-33"></a>

*   <a id="tabs-33-xml"></a>
    
    XML
    
*   <a id="tabs-33-java"></a>
    
    Java
    
*   <a id="tabs-33-shell"></a>
    
    Shell
    

<a id="tabs-33-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <server>
        <url>http://my-node/cache/</url>
      </server>
    </remote>
  </buildCache>
</develocity>
```

<a id="tabs-33-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getRemote().getServer().setUrl(java.net.URI.create("http://my-node/cache/"));
```

<a id="tabs-33-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.remote.url=http://my-node/cache/
```

Note that you still need to configure the address of your Develocity server in the top-level `server` option.

> [!NOTE]
> Similar to the top-level Develocity server configuration, the remote Build Cache server configuration also provides an allowUntrusted option to circumvent certificate warnings: XML Java Shell

> [!WARNING]
> This is a convenient workaround during the initial evaluation, but it’s a serious security issue and shouldn’t be used in production.

<a id="supplying_remote_cache_credentials"></a>

##### Supplying Remote Build Cache Credentials

When using Maven extension version 1.15+, Develocity 2022.3+ and Develocity Build Cache Nodes 13+, the same access key credential used for Build Scan publishing, Test Distribution and other functions can be used for Build Cache access control.

Alternatively, a specific username and password can be specified that will be used instead of any available access key. The username and password can be specified directly in your Develocity configuration, or via a Maven `settings.xml` file.

<a id="tabs-35"></a>

*   <a id="tabs-35-xml"></a>
    
    XML
    
*   <a id="tabs-35-java"></a>
    
    Java
    
*   <a id="tabs-35-shell"></a>
    
    Shell
    

<a id="tabs-35-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <server>
        <credentials>
          <username>my-username</username>
          <password>my-password</password>
        </credentials>
      </server>
    </remote>
  </buildCache>
</develocity>
```

<a id="tabs-35-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getRemote().getServer().getCredentials().setUsername("my-username");
buildCache.getRemote().getServer().getCredentials().setPassword("my-password");
```

<a id="tabs-35-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.remote.username=my-username -Ddevelocity.cache.remote.password=my-password
```

Instead of putting plain text passwords into the configuration file, you should inject them via environment variables as demonstrated below.

**develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <server>
        <credentials>
          <username>${env.DEVELOCITY_CACHE_USERNAME}</username>
          <password>${env.DEVELOCITY_CACHE_PASSWORD}</password>
        </credentials>
      </server>
    </remote>
  </buildCache>
</develocity>
```

<a id="supplying_remote_cache_credentials_via_maven_settings_xml"></a>

##### Supplying Remote Build Cache Credentials via Maven `settings.xml`

Alternatively, you can configure the Build Cache node credentials in the `settings.xml` file, using Maven’s [password encryption](https://maven.apache.org/guides/mini/guide-encryption.html) feature to safely store these credentials.

Note that credentials specified directly via Develocity configuration take precedence over the credentials specified in a `settings.xml` file.

**<user-home>/.m2/settings.xml:**

```
<servers>
  <server>
    <id>my-node</id>
    <username>my-username</username>
    <password>my-password</password>
  </server>
</servers>
```

The Build Cache node with the ID `my-node` can then be referenced in the remote Build Cache configuration.

<a id="tabs-36"></a>

*   <a id="tabs-36-xml"></a>
    
    XML
    
*   <a id="tabs-36-java"></a>
    
    Java
    
*   <a id="tabs-36-shell"></a>
    
    Shell
    

<a id="tabs-36-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <server>
        <id>my-node</id>
        <url>http://my-node/cache/</url>
      </server>
    </remote>
  </buildCache>
</develocity>
```

<a id="tabs-36-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getRemote().getServer().setServerId("my-node");
```

<a id="tabs-36-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.remote.serverId=my-node
```

<a id="disabling_the_remote_cache"></a>

##### Disabling the Remote Build Cache

The remote Build Cache is enabled by default. This can be changed by setting the `enabled` option to `false`.

<a id="tabs-37"></a>

*   <a id="tabs-37-xml"></a>
    
    XML
    
*   <a id="tabs-37-java"></a>
    
    Java
    
*   <a id="tabs-37-shell"></a>
    
    Shell
    

<a id="tabs-37-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <enabled>false</enabled>
    </remote>
  </buildCache>
</develocity>
```

<a id="tabs-37-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getRemote().setEnabled(false);
```

<a id="tabs-37-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.remote.enabled=false
```

<a id="enabling-remote-store"></a>

##### Enabling Remote Store

Since the remote Build Cache is shared with other developers and CI machines, storing in the remote Build Cache is disabled by default. Storing outputs in the remote Build Cache can be enabled by setting the `storeEnabled` option to `true`.

<a id="tabs-38"></a>

*   <a id="tabs-38-xml"></a>
    
    XML
    
*   <a id="tabs-38-java"></a>
    
    Java
    
*   <a id="tabs-38-shell"></a>
    
    Shell
    

<a id="tabs-38-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <storeEnabled>true</storeEnabled>
    </remote>
  </buildCache>
</develocity>
```

<a id="tabs-38-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getRemote().setStoreEnabled(true);
```

<a id="tabs-38-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.remote.storeEnabled=true
```

> [!NOTE]
> In general, the remote Build 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.

<a id="using-expect-continue"></a>

##### Using Expect-Continue

The HTTP Build Cache client allows opt-in use of [HTTP Expect-Continue](https://www.rfc-editor.org/rfc/rfc9110.html#section-10.1.1). This causes PUT requests to happen in two parts: first a check whether a body would be accepted, then transmission of the body if the server indicates it will accept it. This is particularly suitable for Build Cache servers that routinely redirect or reject PUT requests, as it avoids transmitting the cache entry just to have it rejected (e.g. the cache entry is larger than the Build Cache will allow). This additional check incurs extra latency when the server accepts the request, but reduces latency when the request is rejected or redirected.

While the Develocity Build Cache Node supports Expect-Continue, not all HTTP servers and proxies reliably do. Be sure to check that your Build Cache server does support it before enabling.

<a id="tabs-39"></a>

Use Expect-Continue

*   <a id="tabs-39-xml"></a>
    
    XML
    
*   <a id="tabs-39-java"></a>
    
    Java
    
*   <a id="tabs-39-shell"></a>
    
    Shell
    

<a id="tabs-39-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <server>
        <useExpectContinue>true</useExpectContinue>
      </server>
    </remote>
  </buildCache>
</develocity>
```

<a id="tabs-39-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getRemote().getServer().setUseExpectContinue(true);
```

<a id="tabs-39-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.remote.useExpectContinue=true
```

<a id="redirects"></a>

##### Redirects

`3xx` redirecting responses will be followed automatically.

Servers must take care when redirecting `PUT` requests as only `307` and `308` redirect responses will be followed with a `PUT` request. All other redirect responses will be followed with a `GET` request, as per [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#page-54), without the entry payload as the body.

<a id="allowing_insecure_protocols"></a>

##### Allowing Insecure Protocols

Using the remote Build Cache by default enforces the use of HTTPS to make sure your data is only sent via encrypted connections. If the Build Cache server, or any server in a redirect chain, doesn’t use HTTPS the request will be aborted. This validation can be disabled by setting the `allowInsecureProtocol` option to `true`.

<a id="tabs-40"></a>

Allow insecure protocols

*   <a id="tabs-40-xml"></a>
    
    XML
    
*   <a id="tabs-40-java"></a>
    
    Java
    
*   <a id="tabs-40-shell"></a>
    
    Shell
    

<a id="tabs-40-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <buildCache>
    <remote>
      <server>
        <allowInsecureProtocol>true</allowInsecureProtocol>
      </server>
    </remote>
  </buildCache>
</develocity>
```

<a id="tabs-40-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.getRemote().getServer().setAllowInsecureProtocol(true);
```

<a id="tabs-40-shell--panel"></a>

```shell
mvn package -Ddevelocity.cache.remote.allowInsecureProtocol=true
```

> [!WARNING]
> This is a convenient workaround during the initial evaluation, but it’s a serious security issue and shouldn’t be used in production.

<a id="normalization"></a>

### Runtime Classpath Normalization

A common pattern is to have some build logic that writes volatile data to some files. For example your build might write a timestamp to a `build.properties` file. This helps to identify the source of a build artifact. However, this also causes problems with build caching: when Maven executes tests, the runtime classpath becomes part of the cache key for that test run. Any resource file that’s processed by Maven is also added to that runtime classpath. In consequence, if a build generates a build timestamp, this causes the runtime classpath to change on every build invocation, resulting in cache misses. To circumvent this situation the Maven extension provides several configuration options to normalize the runtime classpath in order to deal with this volatility.

<a id="ignoring-arbitrary-files"></a>

#### Ignoring Arbitrary Files

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.

<a id="tabs-41"></a>

*   <a id="tabs-41-xml"></a>
    
    XML
    
*   <a id="tabs-41-java"></a>
    
    Java
    

<a id="tabs-41-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <normalization>
            <runtimeClassPath>
              <ignoredFiles>
                <ignoredFile>META-INF/build.properties</ignoredFile>
              </ignoredFiles>
            </runtimeClassPath>
          </normalization>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-41-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerNormalizationProvider(context -> context
    .configureRuntimeClasspathNormalization(
        normalization -> normalization
            .setIgnoredFiles("META-INF/build.properties")
    ));
```

> [!NOTE]
> Files matching the patterns META-INF/maven/\*\*/pom.xml and META-INF/maven/\*\*/pom.properties are always ignored.

<a id="ignoring-specific-entries-in-properties-files"></a>

#### Ignoring Specific Entries in Properties Files

Since properties files are a common file format to store data generated by the build, the normalization DSL provides special support for ignoring specific entries in properties files. Again, ANT-style patterns can be used to match relevant properties files. In the following example any files named `build.properties` in `com/example` or any subdirectory are matched and the value of the property `build.timestamp` is ignored:

<a id="tabs-42"></a>

*   <a id="tabs-42-xml"></a>
    
    XML
    
*   <a id="tabs-42-java"></a>
    
    Java
    

<a id="tabs-42-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <normalization>
            <runtimeClassPath>
              <propertiesNormalizations>
                <propertiesNormalization>
                  <path>com/example/**/build.properties</path>
                  <ignoredProperties>
                    <ignore>build.timestamp</ignore>
                  </ignoredProperties>
                </propertiesNormalization>
              </propertiesNormalizations>
            </runtimeClassPath>
          </normalization>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-42-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerNormalizationProvider(context -> context
    .configureRuntimeClasspathNormalization(
        normalization -> normalization
            .addPropertiesNormalization("com/example/**/build.properties", "build.timestamp")
    ));
```

A common location to store properties files is the META-INF directory. Thus, the normalization DSL provides a shortcut for normalizing files matching `META-INF/**/*.properties`

<a id="tabs-43"></a>

*   <a id="tabs-43-xml"></a>
    
    XML
    
*   <a id="tabs-43-java"></a>
    
    Java
    

<a id="tabs-43-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <normalization>
            <runtimeClassPath>
              <metaInf>
                <ignoredProperties>
                  <ignore>app.version</ignore>
                </ignoredProperties>
              </metaInf>
            </runtimeClassPath>
          </normalization>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-43-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerNormalizationProvider(context -> context
    .configureRuntimeClasspathNormalization(
        normalization -> normalization
            .configureMetaInf(metaInf -> metaInf.setIgnoredProperties("app.version"))
    ));
```

<a id="normalizing-contents-of-meta-inf"></a>

#### Normalizing Contents of META-INF

The `<metaInf>` configuration element offers even more convenience configuration settings for normalizing the contents of the META-INF directory. For example, you may only want to ignore one or more attributes in MANIFEST files, e.g. `Implementation-Version` instead of ignoring the whole file. This can be done as follows:

<a id="tabs-44"></a>

*   <a id="tabs-44-xml"></a>
    
    XML
    
*   <a id="tabs-44-java"></a>
    
    Java
    

<a id="tabs-44-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <normalization>
            <runtimeClassPath>
              <metaInf>
                <ignoredAttributes>
                  <ignore>Implementation-Version</ignore>
                </ignoredAttributes>
              </metaInf>
            </runtimeClassPath>
          </normalization>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-44-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerNormalizationProvider(context -> context
    .configureRuntimeClasspathNormalization(
        normalization -> normalization
            .configureMetaInf(metaInf -> metaInf.setIgnoredAttributes("Implementation-Version"))
    ));
```

If you want to ignore MANIFEST files completely there’s the `<ignoreManifest>` configuration as a shorthand for that. It’s the equivalent of adding `META-INF/MANIFEST.MF` as an ignored file.

<a id="tabs-45"></a>

*   <a id="tabs-45-xml"></a>
    
    XML
    
*   <a id="tabs-45-java"></a>
    
    Java
    

<a id="tabs-45-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <normalization>
            <runtimeClassPath>
              <metaInf>
                <ignoreManifest>true</ignoreManifest>
              </metaInf>
            </runtimeClassPath>
          </normalization>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-45-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerNormalizationProvider(context -> context
    .configureRuntimeClasspathNormalization(
        normalization -> normalization
            .configureMetaInf(metaInf -> metaInf.setIgnoreManifest(true))
    ));
```

In situations where you have several changing files in the META-INF directory, you might want to ignore the contents of that directory completely. The `<ignoreCompletely>` configuration is a shorthand for adding `META-INF/**` to ignored files.

<a id="tabs-46"></a>

*   <a id="tabs-46-xml"></a>
    
    XML
    
*   <a id="tabs-46-java"></a>
    
    Java
    

<a id="tabs-46-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <normalization>
            <runtimeClassPath>
              <metaInf>
                <ignoreCompletely>true</ignoreCompletely>
              </metaInf>
            </runtimeClassPath>
          </normalization>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-46-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerNormalizationProvider(context -> context
    .configureRuntimeClasspathNormalization(
        normalization -> normalization
            .configureMetaInf(metaInf -> metaInf.setIgnoreCompletely(true))
    ));
```

> [!WARNING]
> The more you ignore using runtime classpath normalization, the more likely false cache hits become. You should use these features with care and try to narrow down the scope of normalizations as much as possible, e.g. you should prefer normalizing a single MANIFEST attribute over ignoring the META-INF directory completely.

<a id="system_property_normalization"></a>

### System Property Normalization

It’s common to use system properties as vehicles to transport information between different executions. It’s also not unheard of that these system properties don’t have a direct effect on the results of the execution of specific goals.

One example would be having a temporary folder name or a timestamp passed to a test goal. These will change in each execution, but in certain cases, don’t determine the result of these executions. These are the cases where system property normalization can be used.

<a id="ignoring-system-properties-by-keys"></a>

#### Ignoring System Properties by Keys

The normalization provides a way to ignore certain system properties by their key

<a id="tabs-47"></a>

*   <a id="tabs-47-xml"></a>
    
    XML
    
*   <a id="tabs-47-java"></a>
    
    Java
    

<a id="tabs-47-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <normalization>
            <systemProperties>
              <ignoredKeys>
                <ignore>examplePropertyName</ignore>
              </ignoredKeys>
            </systemProperties>
          </normalization>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-47-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerNormalizationProvider(context -> context
    .configureSystemPropertiesNormalization(
        normalization -> normalization
            .setIgnoredKeys("examplePropertyName")
    ));
```

<a id="declaring-inputs-and-outputs"></a>

### Declaring Inputs and Outputs

The Build Cache works based on inputs and outputs. For each supported goal execution a cache key is calculated by inspecting all inputs. The cache key is then used to lookup the outputs of that execution in the cache. If no result can be found, the goal is executed and the outputs are stored in the Build Cache under the cache key. This section explains how input and outputs can be fine-tuned.

The extension already configures caching for some plugins and goals out of the box. A list of those plugins and goals can be seen in [Cacheable plugins and goals](#cacheable_plugins_and_goals).

<a id="adding_inputs_and_outputs"></a>

#### Adding Additional Inputs and Outputs to a Plugin or Execution

The predefined inputs and outputs of any supported goal can be augmented by declaring them in the `<pluginManagement>` section of your `pom.xml` file. A common use case for declaring additional input files are test cases that read from a location that isn’t automatically tracked. For example, you might have [Cucumber](https://cucumber.io) specification files located in the `src/test/specs` directory. Any change to the files in that directory may change the result of running the tests. So changing specification files should result in rerunning the tests and not loading the results from the cache. Without additional configuration the Build Cache is unaware of these additional inputs and will therefore load the result from the Build Cache even if specification files change.

<a id="declaring-additional-inputs"></a>

##### Declaring Additional Inputs

The following will add the directory `src/test/specs` as an input to all executions of the [Apache Maven Surefire plugin](https://maven.apache.org/surefire/maven-surefire-plugin/). Any files in that folder which match the given includes and excludes will then be tracked as part of the cache key:

<a id="tabs-48"></a>

*   <a id="tabs-48-xml"></a>
    
    XML
    
*   <a id="tabs-48-java"></a>
    
    Java
    

<a id="tabs-48-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <inputs>
                <fileSets>
                  <fileSet>
                    <name>specs</name>
                    <paths>
                      <path>src/test/specs</path>
                    </paths>
                    <includes>
                      <include>**/*.feature</include>
                    </includes>
                    <excludes>
                      <exclude>archive/**/*.feature</exclude>
                    </excludes>
                    <normalization>RELATIVE_PATH</normalization>
                  </fileSet>
                </fileSets>
              </inputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-48-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-surefire-plugin",
        () -> context.inputs(inputs -> inputs
            .fileSet(
                "specs",
                "src/test/specs",
                fileSet -> fileSet
                    .include("**/*.feature")
                    .exclude("archive/**/*.feature")
                    .normalizationStrategy(MojoMetadataProvider.Context.FileSet.NormalizationStrategy.RELATIVE_PATH)
            ))
    );
});
```

While processing inputs, various normalization strategies can be applied in order to raise the effectiveness of the Build Cache. In other words, normalization strategies are a way of ignoring changes to input files that are irrelevant for the goal execution. The following input normalization strategies are supported:

   
| Normalization Strategy | Description | ignoreEmptyDirectories | ignoreLineEndings |
| --- | --- | --- | --- |
| IGNORED\_PATH | Considers the full content of files, but ignores their path. | Not Supported | Supported |
| NAME\_ONLY | Considers the full content of files, but only tracks their name and not the rest of their path. | Supported | Supported |
| 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. | Supported | Supported |
| 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. | Supported | Supported |
| CLASSPATH | Considers only the information relevant for running Java code. | Not Supported | Supported |
| 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. | Not Supported | Not Supported |

<a id="ignoring-empty-directories"></a>

##### Ignoring Empty Directories

The `NAME_ONLY`, `RELATIVE_PATH`, and `ABSOLUTE_PATH` strategies support additional normalization options. Their sensitivity to the presence of empty directories in the source tree can be controlled. The default is to take empty directories into account. This means that adding an empty directory to the source tree will result in a cache miss. The following configuration causes empty directories to be ignored:

<a id="tabs-49"></a>

*   <a id="tabs-49-xml"></a>
    
    XML
    
*   <a id="tabs-49-java"></a>
    
    Java
    

<a id="tabs-49-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <inputs>
                <fileSets>
                  <fileSet>
                    <name>specs</name>
                    <paths>
                      <path>src/test/specs</path>
                    </paths>
                    <normalization>
                      <strategy>RELATIVE_PATH</strategy>
                      <ignoreEmptyDirectories>true</ignoreEmptyDirectories> (1)
                    </normalization>
                  </fileSet>
                </fileSets>
              </inputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-49-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-surefire-plugin",
        () -> context.inputs(inputs -> inputs
            .fileSet(
                "specs",
                "src/test/specs",
                fileSet -> fileSet
                    .normalizationStrategy(MojoMetadataProvider.Context.FileSet.NormalizationStrategy.RELATIVE_PATH)
                    .emptyDirectoryHandling(MojoMetadataProvider.Context.FileSet.EmptyDirectoryHandling.IGNORE) (1)
            ))
    );
});
```

1. Mark empty directories as ignorable

> [!NOTE]
> A common reason for having empty directories in the source tree is using a version control system like Git that only keeps tracks of files. When one developer deletes a directory tree on their machine and another developer pulls that change from the repository, Git will only delete the files contained in that directory tree leaving parent directories empty.

<a id="ignoring-line-endings"></a>

##### Ignoring Line Endings

The `ignoreLineEndings` option allows authors to specify that line endings in text files should be normalized for Build Cache checks, so that files that only differ by line endings will be considered identical. Binary files, however, won’t be affected by this normalization.

> [!NOTE]
> Line ending normalization only applies to text files encoded with the ASCII character set or one of its supersets (e.g., UTF-8). Text files encoded in a non-ASCII character set (e.g., UTF-16) will be treated as binary files and won’t be subject to line ending normalization.

<a id="tabs-50"></a>

*   <a id="tabs-50-xml"></a>
    
    XML
    
*   <a id="tabs-50-java"></a>
    
    Java
    

<a id="tabs-50-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <inputs>
                <fileSets>
                  <fileSet>
                    <name>specs</name>
                    <paths>
                      <path>src/test/specs</path>
                    </paths>
                    <normalization>
                      <strategy>RELATIVE_PATH</strategy>
                      <ignoreLineEndings>true</ignoreLineEndings> (1)
                    </normalization>
                  </fileSet>
                </fileSets>
              </inputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-50-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-surefire-plugin",
        () -> context.inputs(inputs -> inputs
            .fileSet(
                "specs",
                "src/test/specs",
                fileSet -> fileSet
                    .normalizationStrategy(MojoMetadataProvider.Context.FileSet.NormalizationStrategy.RELATIVE_PATH)
                    .lineEndingHandling(MojoMetadataProvider.Context.FileSet.LineEndingHandling.NORMALIZE) (1)
            ))
    );
});
```

1. Mark line endings as ignorable

All strategies except `COMPILE_CLASSPATH` support this option.

The Maven extension normalizes line endings in source files when computing the Build Cache key for `compile` or `testCompile` goals of the [maven-compiler-plugin](#maven_compiler_plugin).

<a id="declaring-additional-outputs"></a>

##### Declaring Additional Outputs

Cucumber can be configured to generate various types of reports. These reports may be located in an output folder different from the default test report folder. In order to store them in the Build Cache for later retrieval the output location needs to be declared as an additional output. The following will add the additional output directory `${project.build.directory}/cucumber` to the `cucumber-tests` execution of the surefire plugin. The contents of this directory will become part of the Build Cache archive for this execution.

<a id="tabs-51"></a>

*   <a id="tabs-51-xml"></a>
    
    XML
    
*   <a id="tabs-51-java"></a>
    
    Java
    

<a id="tabs-51-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <executions>
                <execution>
                  <id>cucumber-tests</id>
                  <outputs>
                    <directories>
                      <directory>
                        <name>cucumber-reports</name>
                        <path>${project.build.directory}/cucumber</path>
                      </directory>
                    </directories>
                  </outputs>
                </execution>
              </executions>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-51-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-surefire-plugin", () -> {
            if ("cucumber-tests".equals(context.getMojoExecution().getExecutionId())) {
                context.outputs(outputs -> outputs.directory("cucumber-reports", "${project.build.directory}/cucumber"));
            }
        }
    );
});
```

> [!NOTE]
> Configuring caching for a specific goal execution isn’t limited to declaring additional outputs. The same constructs for defining additional inputs that have been shown before can also be used for specific goal executions.

<a id="overriding-inputs-and-outputs"></a>

##### Overriding Inputs and Outputs

The predefined inputs and outputs of any [supported goal](#cacheable_plugins_and_goals) that are used by the Develocity Maven extension can be overridden, i.e. redefined with a custom value. A common use case is to override the value of an input property because it contains an absolute path that would prevent cache hits on other machines. For example, the maven-checkstyle-plugin has a `propertyExpansion` input property that may contain an absolute path in some use cases.

```xml
<plugin>
  <artifactId>maven-checkstyle-plugin</artifactId>
  <configuration>
    <propertyExpansion>checkstyle.additional.suppressions.file=${project.basedir}/custom-suppressions.xml</propertyExpansion>
  </configuration>
</plugin>
```

In this case, the `propertyExpansion` input property can be redefined for the purpose of cache key computation in Develocity while the Maven goal’s implementation will continue to read the existing configuration.

<a id="tabs-52"></a>

*   <a id="tabs-52-xml"></a>
    
    XML
    
*   <a id="tabs-52-java"></a>
    
    Java
    

<a id="tabs-52-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <artifactId>maven-checkstyle-plugin</artifactId>
              <inputs>
                <properties>
                  <property>
                    <name>propertyExpansion</name>
                    <value>checkstyle.additional.suppressions.file=ignored</value> (1)
                  </property>
                </properties>
                <fileSets>
                  <fileSet>
                    <name>checkstyle.additional.suppressions.file</name>
                    <paths>
                      <path>${project.basedir}/custom-suppressions.xml</path> (2)
                    </paths>
                    <normalization>
                      <strategy>IGNORED_PATH</strategy>
                      <ignoreLineEndings>true</ignoreLineEndings>
                    </normalization>
                  </fileSet>
                </fileSets>
              </inputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-52-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-checkstyle-plugin",
        () -> context.inputs(inputs -> inputs
            .property("propertyExpansion", "checkstyle.additional.suppressions.file=ignored") (1)
            .fileSet(
                "checkstyle.additional.suppressions.file",
                "${project.basedir}/custom-suppressions.xml", (2)
                fileSet -> fileSet
                    .normalizationStrategy(MojoMetadataProvider.Context.FileSet.NormalizationStrategy.IGNORED_PATH)
                    .lineEndingHandling(MojoMetadataProvider.Context.FileSet.LineEndingHandling.NORMALIZE)
            )
        )
    );
});
```

1. Redefine value of propertyExpansion input property
2. Track contents of ${project.basedir}/custom-suppressions.xml as inputs ignoring the file name and the type of line endings

<a id="disabling_build_caching_for_a_plugin_or_execution"></a>

#### 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:

<a id="tabs-53"></a>

*   <a id="tabs-53-xml"></a>
    
    XML
    
*   <a id="tabs-53-java"></a>
    
    Java
    

<a id="tabs-53-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <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>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-53-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-failsafe-plugin",
        () -> context.outputs(outputs -> outputs.notCacheableBecause("these tests verify integration with other systems and should rerun even if our inputs didn't change"))
    );
});
```

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.

<a id="tabs-54"></a>

*   <a id="tabs-54-xml"></a>
    
    XML
    
*   <a id="tabs-54-java"></a>
    
    Java
    

<a id="tabs-54-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <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>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-54-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-failsafe-plugin", () -> {
            if ("systems-integration-test".equals(context.getMojoExecution().getExecutionId())) {
                context.outputs(outputs -> outputs.notCacheableBecause("these tests verify integration with other systems and should rerun even if our inputs didn't change"));
            }
        }
    );
});
```

<a id="disabling_storing_outputs_for_specific_goal"></a>

#### Disabling Storing Outputs for a Specific Goal or Execution

By default, outputs are stored in the Build Cache if it’s enabled and the build includes the `clean` lifecycle phase. Disabling storing outputs of a specific goal can be achieved by setting the `storeEnabled` option to `false` on the `outputs` configuration of the goal’s plugin or execution.

<a id="tabs-55"></a>

*   <a id="tabs-55-xml"></a>
    
    XML
    
*   <a id="tabs-55-java"></a>
    
    Java
    

<a id="tabs-55-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <outputs>
                <storeEnabled>false</storeEnabled>
              </outputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-55-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-compiler-plugin", () -> context.outputs(outputs -> outputs.storeEnabled(false)));
});
```

An example use case is when local development has the side effect of corrupting outputs of a goal (e.g. when the IDE and Maven use the same compilation output directory). Without disabling storing outputs to the Build Cache, this can lead to issues with corrupt cache entries being loaded which make downstream goals fail (to resolve such a situation you can use [the `-DrerunGoals` command line argument](#rerunning_goals)). To avoid this situation, the above sample configuration for the compile goal disables storing its outputs in a local-only enabled Maven profile. On CI, outputs will still be stored in the Build Cache so that local developer builds are able to reuse cache entries from prior CI builds.

The following example shows how to configure the local profile to disable storing outputs generated by the compile goal. To check if the build is executed locally we check that the environment variable `CI` isn’t set.

<a id="tabs-56"></a>

*   <a id="tabs-56-xml"></a>
    
    XML
    
*   <a id="tabs-56-java"></a>
    
    Java
    

<a id="tabs-56-xml--panel"></a>

```xml
<profiles>
  <profile>
    <id>local</id>
    <activation>
      <property>
        <name>!env.CI</name>
      </property>
    </activation>
    <build>
      <pluginManagement>
        <plugins>
          <plugin>
            <groupId>com.gradle</groupId>
            <artifactId>develocity-maven-extension</artifactId>
            <configuration>
              <develocity>
                <plugins>
                  <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <outputs>
                      <storeEnabled>false</storeEnabled>
                    </outputs>
                  </plugin>
                </plugins>
              </develocity>
            </configuration>
          </plugin>
        </plugins>
      </pluginManagement>
    </build>
  </profile>
</profiles>
```

<a id="tabs-56-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-compiler-plugin", () -> {
        if (!Boolean.parseBoolean(System.getenv("CI"))) {
            context.outputs(outputs -> outputs.storeEnabled(false));
        }
    });
});
```

<a id="conditionally_skipping_goal_execution"></a>

#### Conditionally Skipping a Goal Execution

You can conditionally skip a goal execution by configuring the `skipIfTrue` option with one or several `Mojo` properties of type `boolean`. If the value of any configured property is `true`, then the goal execution will be skipped.

<a id="tabs-57"></a>

*   <a id="tabs-57-xml"></a>
    
    XML
    
*   <a id="tabs-57-java"></a>
    
    Java
    

<a id="tabs-57-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-enforcer-plugin</artifactId>
              <skipIfTrue>
                <property>skip</property> (1)
              </skipIfTrue>
              <executions>
                <execution>
                  <id>enforce-banned-dependencies</id>
                  <skipIfTrue>
                    <property>skip</property> (2)
                  </skipIfTrue>
                </execution>
              </executions>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-57-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("maven-enforcer-plugin", () -> {
        context.skipIfTrue("skip"); (1)
        if ("enforce-banned-dependencies".equals(context.getMojoExecution().getExecutionId())) {
            context.skipIfTrue("skip"); (2)
        }
    });
});
```

1. Will skip all executions of maven-enforcer-plugin if skip property is set
2. Will skip only specific execution of maven-enforcer-plugin if skip property is set

An example use case is a goal that supports skipping of its execution via setting a specific boolean property. Even if a goal execution is skipped via such a property, the Build Cache will still attempt to retrieve outputs from a previous execution. In order to avoid unnecessary computation and network overhead, the name of the specific boolean property can be declared via `skipIfTrue`.

All the [supported goals](#cacheable_plugins_and_goals) are already configured to skip the Build Cache if the goal is skipped.

<a id="making_other_goals_cacheable"></a>

#### 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 or a Map of complex values) configuration.

<a id="tabs-58"></a>

*   <a id="tabs-58-xml"></a>
    
    XML
    
*   <a id="tabs-58-java"></a>
    
    Java
    

<a id="tabs-58-xml--panel"></a>

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <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>
              </inputs>
              <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>
              <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>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="tabs-58-java--panel"></a>

**Using the Programmatic Configuration:**

```
buildCache.registerMojoMetadataProvider(context -> {
    context.withPlugin("awesome-but-slow-plugin", () -> {
            if ("my.company".equals(context.getMojoExecution().getPlugin().getGroupId())) {
                context.inputs(inputs -> inputs
                        .fileSet("sources", fileSet -> fileSet.includesProperty("includes").excludesProperty("excludes"))
                        .properties("encoding")
                        .ignore("logWarnings")
                    )
                    .nested("forkOptions", nestedContext -> nestedContext.inputs(inputs -> inputs.properties("maxHeap")))
                    .iterate("targetPlatforms", iteratedContext -> iteratedContext.inputs(inputs -> inputs.properties("architecture", "linkingMode")))
                    .outputs(outputs -> outputs.directory("outputDir").cacheable("This plugin has CPU-bound goals with well-defined inputs and outputs"));
            }
        }
    );
});
```

<a id="solving_problems_with_build_caching"></a>

### Troubleshooting

While working with the Build Cache you may encounter situations where build results aren’t retrieved from the Build Cache, although you would expect them to. This section provides guidance for analyzing and solving these problems.

<a id="debugging-cache-operations"></a>

#### Debugging Cache Operations

The extension provides several loggers to make analyzing problems with build caching easier. To show the effective Build Cache configuration, use the `develocity.goal.cache` logger:

```shell
mvn clean verify -Dorg.slf4j.simpleLogger.log.develocity.goal.cache=debug
  [DEBUG] Using the Build Cache with the following configuration:
    Local Build Cache: enabled
        directory: /Users/johndoe/.m2/.develocity/build-cache
        cleanup: enabled
            retention: 168h
            interval: 24h
    Remote Build Cache: enabled
        url: https://my-server/cache/
        authenticated: false
        storeEnabled: false
        allowUntrustedServer: false
        useExpectContinue: false
```

The `develocity.goal.cache` logger will also print the result of determining the cacheability of the executed goals:

**Output:**

```
[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
```

> [!NOTE]
> All information printed by the develocity.goal.cache can also be viewed in the Build Scan for that build.

<a id="finding-the-cause-of-cache-misses"></a>

#### Finding the Cause of Cache Misses

Sometimes you might encounter a situation where a goal execution isn’t avoided by using the Build 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](#cacheable_plugins_and_goals) should be retrieved from the local Build Cache (if it’s enabled). If this isn’t 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 once the Build Scans have been published, they can be compared in Develocity. 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.

![Example of Comparing the Build Scan Data of Two Builds](https://docs.gradle.com/develocity/maven/2.4/maven-extension/../_images/comparison-unstable-input.png)

Example of Comparing the Build Scan Data of Two Builds

Once the changing input is identified, the build can be changed to be reproducible or [normalization](#normalization) can be used to ignore the changing input.

<a id="solving_common_causes_of_cache_misses"></a>

#### 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.

<a id="jaxb"></a>

##### 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](https://github.com/javaee/jaxb-v2/issues/598). 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](https://www.mojohaus.org/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](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/xjc.html) which is `false` by default (= always generate a header). To prevent this add the corresponding configuration to the Maven plugin you use, for example:

```xml
<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](https://github.com/highsource/maven-jaxb2-plugin) it’s still a good idea to remove unnecessary instability from its outputs. However, the Develocity Maven extension [supports caching its `generate` goal](#maven-jaxb2-plugin) even if file headers are being generated. Thus, downstream goals won’t be affected by its changing outputs when they’re loaded from the cache.

Some 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](https://www.mojohaus.org/jaxb2-maven-plugin/) and the [maven-jaxb2-plugin](https://github.com/highsource/maven-jaxb2-plugin/wiki/Controlling-the-Output) provide a `<locale>` option for this.

<a id="maven-bundle-plugin"></a>

##### maven-bundle-plugin

Older versions of the [maven-bundle-plugin](https://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html) generate a timestamp in the `MANIFEST.MF` file. To prevent this, use version `5.1.5`\+ of the plugin or adjust the plugin configuration as following:

```xml
<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 underscore in `<_removeheaders>` isn’t a typo.

<a id="maven-resources-plugin"></a>

##### maven-resources-plugin

A common pattern is to write a build timestamp to a `build.properties` file using [Maven resource filtering](https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html). One way to fix this is using [normalization](#normalization) to ignore the file. Alternatively the build can be adjusted by moving the generation of the timestamp to a separate profile that’s only executed when creating a release:

**build.properties:**

```
build.timestamp=${timestamp}
```

**pom.xml:**

```
<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>
```

<a id="maven-surefire-plugin"></a>

##### maven-surefire-plugin

<a id="jar-file-in-test-classpath"></a>

###### JAR File in Test Classpath

By default, the test runtime classpath used by the `maven-surefire-plugin` contains the classes directory of project dependencies if the build has been started with the `mvn clean test` command. If the build was started using `mvn clean package` or a later lifecycle phase, then, instead of the classes directory, the test runtime classpath will contain the JAR file. Such behavior might lead to unexpected cache misses.

To prevent such a situation you can configure the `maven-jar-plugin` to produce the JAR file before the `test` phase. For example by binding the `default-jar` execution to the `process-test-resources` phase:

**pom.xml:**

```
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <executions>
    <execution>
      <id>default-jar</id>
      <phase>process-test-resources</phase>
    </execution>
  </executions>
</plugin>
```

Such a change will force the `maven-surefire-plugin` to always use the JAR file and will help to avoid cache misses.

<a id="using-with-native-maven-plugin-from-graalvm"></a>

###### Using With native-maven-plugin From GraalVM

Using the `native-maven-plugin` from `org.graalvm.buildtools` will prevent caching the output of the Surefire test goals. The `native-maven-plugin` will register an untracked output folder under `target/test-ids` which causes the goals to become uncacheable. To solve this, declare the folder as an output for the Surefire plugin in the configuration of the `develocity-maven-extension` as follows:

**pom.xml:**

```
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <outputs>
                <directories>
                  <directory>
                    <name>test-ids</name>
                    <path>${project.build.directory}/test-ids</path>
                  </directory>
                </directories>
              </outputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

<a id="using-system-properties"></a>

###### Using System Properties

When the `maven-surefire-plugin` is used with `<systemPropertyVariables>` defined in the plugin’s configuration, if any of the defined properties contain a path, this path must be declared as an extra input/output parameter on the ``develocity-plugin’s `surefire-plugin`` related configuration. When the paths aren’t declared the goal won’t be cacheable and the goal’s cacheability reason will display `Goal execution marked as not cacheable: Build caching was not enabled for this goal execution because system property 'samplesDir' with value '$path' was used as a command line argument, but not declared as an input or output`.

The following example shows 3 system properties being configured and how to declare them respectively for cacheability.

**pom.xml:**

```
<build>
  <!-- ... -->
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.5.5</version>
      <configuration>
        <systemPropertyVariables>
          <test.project.directory>${test.project.directory}</test.project.directory>
          <test.output.directory>${test.output.directory}</test.output.directory>
          <project.build.directory.testids>${project.build.directory}/test-ids</project.build.directory.testids>
        </systemPropertyVariables>
      </configuration>
    </plugin>
  </plugins>

  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>com.gradle</groupId>
        <artifactId>develocity-maven-extension</artifactId>
        <configuration>
          <develocity>
            <plugins>
              <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <inputs>
                  <fileSets>
                    <fileSet>
                      <name>generatedTestDir</name>
                      <paths>
                        <path>${test.project.directory}</path>
                      </paths>
                    </fileSet>
                  </fileSets>
                </inputs>
                <outputs>
                  <directories>
                    <directory>
                      <name>testCacheDir</name>
                      <path>${test.output.directory}</path>
                    </directory>
                    <directory>
                      <name>testIdsDir</name>
                      <path>${project.build.directory}/test-ids</path>
                    </directory>
                  </directories>
                </outputs>
              </plugin>
            </plugins>
          </develocity>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>
```

Alternatively normalization could be used to ignore certain system properties. Refer to the [system property normalization](#system_property_normalization) section of the extension user manual for details.

<a id="maven-failsafe-plugin"></a>

##### 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](https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#summaryFile). 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:

**pom.xml:**

```
<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](https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#reportsDirectory), for example:

**pom.xml:**

```
<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>
```

<a id="maven_compiler_plugin_on_non_clean_builds"></a>

##### maven-compiler-plugin

When invoking the build without the `clean` lifecycle, the Build Cache is in [read-only mode](#disabling_local_store). Existing cache entries produced by earlier builds are then reused but no new entries are stored. This works for all Maven goals that are supported out of the box with the exception of the maven-compiler-plugin. Since the maven-compiler-plugin can be used in conjunction with other plugins that produce class files (e.g. when also using another JVM language like Kotlin or Groovy in the same project), it uses the classes output directory (usually `target/classes`) as an input. Therefore, builds without `clean` will have different inputs and thus a different cache key.

<a id="use-pom-xml-merge-strategies-to-solve-misconfigurations"></a>

#### Use `pom.xml` Merge Strategies to Solve Misconfigurations

When there is a [pom.xml](#pom) configuration for the Develocity Maven extension on a parent `pom.xml`, such as runtime classpath normalization or declaring inputs and outputs for given plugins, and another configuration on a child `pom.xml`, configurations may merge incorrectly, resulting in a misconfiguration of the Develocity Maven extension. To see the effective `pom.xml` that’s used during the execution of the build on any given project `mvn help:effective-pom` can be executed, and the effective `pom.xml` is printed to the console. If a check of the Develocity Maven extension configuration shows that it’s invalid or not properly merged, then a different merge strategy can be applied as mentioned in this [blog post](https://www.sonatype.com/blog/2011/01/maven-how-to-merging-plugin-configuration-in-complex-projects).

In this example, we’ve a parent project and a child project which both have specific Develocity configurations for the compiler and Surefire mojos. The following excerpt is from the parent `pom.xml`:

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <version>2.4.0</version>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <executions>
                <execution>
                  <id>default-compile</id>
                  <outputs>
                    <notCacheableBecause>something</notCacheableBecause> (1)
                  </outputs>
                </execution>
              </executions>
            </plugin>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <inputs>
                <properties>
                  <property>
                    <name>additionalProperty</name> (2)
                    <value>${additionalProperty}</value>
                  </property>
                </properties>
              </inputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

1. Parent POM configures compiler mojo to be not cacheable
2. Parent POM configures Surefire mojo to track an additionalProperty as input

This excerpt is from the child `pom.xml` where we want to keep the configuration of the compiler mojo defined on the parent `pom.xml` and add another property as input to the Surefire mojo:

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <version>2.4.0</version>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId> (1)
              <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <inputs>
                <properties combine.children="append">
                  <property>
                    <name>anotherAdditionalProperty</name> (2)
                    <value>${anotherAdditionalProperty}</value>
                  </property>
                </properties>
              </inputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

1. Child POM configures to inherit compiler mojo configuration from parent POM
2. Child POM configures Surefire mojo to track an anotherAdditionalProperty as input

Due to the merging strategies available for Maven POM files there are several pitfalls:

1.  If a child POM file wants to inherit the configuration of a parent POM file, it needs to declare the same plugins in the exact same order as in the parent POM such that Maven properly merges the two POM files.
    
2.  If some elements of a parent POM need to be extended in a child POM, the above explained merges strategies should be taken into account. In the example child POM we use the `combine.children="append"` merge strategy to append the `anotherAdditionalProperty` input to the existing `additionalProperty` input declared on the parent POM.
    

With the above setup the merged child POM produced by retrieving the effective POM via executing `mvn help:effective-pom` resulted in the following merged child POM:

```xml
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>com.gradle</groupId>
      <artifactId>develocity-maven-extension</artifactId>
      <version>2.4.0</version>
      <configuration>
        <develocity>
          <plugins>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <executions>
                <execution>
                  <id>default-compile</id>
                  <outputs>
                    <notCacheableBecause>something</notCacheableBecause> (1)
                  </outputs>
                </execution>
              </executions>
            </plugin>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <inputs>
                <properties combine.children="append"> (2)
                  <property>
                    <name>additionalProperty</name>
                    <value>${additionalProperty}</value>
                  </property>
                  <property>
                    <name>anotherAdditionalProperty</name>
                    <value>${anotherAdditionalProperty}</value>
                  </property>
                </properties>
              </inputs>
            </plugin>
          </plugins>
        </develocity>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
```

1. Merged child POM inherits the compiler mojo configuration from parent POM
2. Merged child POM configures Surefire mojo to track both properties as inputs

<a id="rerunning_goals"></a>

#### Rerunning Goals to Deal With Invalid Cache Entries

In rare circumstances the Build 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.

<a id="symlinks"></a>

#### Handling Symbolic Links

When working with Git repositories containing symbolic links, ensure that CI and local builds clone the project with the same value for the `core.symlinks` [Git parameter](https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks). This parameter is typically `true` by default on most platforms, but it can be `false` on Windows. Setting it to `true` with `git config --global core.symlinks true` (before cloning the Git repository) ensures consistent cache content across environments, avoiding potential exceptions when unpacking cache entries.

<a id="test_distribution"></a>

## Using Test Distribution

Develocity Test Distribution takes your existing test suites and distributes them across remote agents to execute them faster.

> [!TIP]
> For information on how to use Test Distribution, consult the [Test Distribution User Manual](https://docs.gradle.com/develocity/test-distribution/3.7/).

<a id="predictive_test_selection"></a>

## Using Predictive Test Selection

Develocity Predictive Test Selection allows developers to get faster feedback by running only tests that are likely to provide useful feedback on a particular code change using a probabilistic machine learning model.

> [!TIP]
> For information on how to use Predictive Test Selection, consult the [Predictive Test Selection User Manual](https://docs.gradle.com/develocity/2026.1/using-develocity/predictive-test-selection/).

<a id="test_retry"></a>

## Using Test Retry

The Maven Surefire and Failsafe plugins provide configuration properties which cause the test runner to retry each failing test a configured number of times.

Configuring these properties in your project causes failing tests to be rerun immediately after they fail. If a test passes and then fails, Develocity will record a `FLAKY` outcome for the test.

**pom.xml:**

```
<properties>
    <failsafe.rerunFailingTestsCount>2</failsafe.rerunFailingTestsCount>
    <surefire.rerunFailingTestsCount>2</surefire.rerunFailingTestsCount>
</properties>
```

> [!TIP]
> For more information on how to detect flaky tests with test retry, consult the [Develocity Flaky Test Detection Guide](https://docs.gradle.com/develocity/2026.1/guides/flaky-test-detection-guide/).

* * *

If you have any questions or need any assistance contact the Develocity support team or your customer success representative.

<a id="configuration_reference"></a>

## Appendix A: Configuration Reference

<a id="develocity_xml"></a>

### develocity.xml

Most aspects of the Develocity Maven extension are configured in the `develocity.xml` configuration file. Some options can be overwritten by system properties.

The `develocity.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/develocity.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 `-Ddevelocity.global.config` argument. This can be useful for CI environments where changing the Maven installation is not possible.
    
*   `<classpath>/develocity.xml` is used for organization-wide or team-wide configuration and overrides the global configuration. This allows to package a `develocity.xml` file in the root of a [custom extension jar](#custom_extension) that can be reused across projects.
    
*   `<project-dir>/.mvn/develocity.xml` is used for project-specific configuration and overrides the classpath configuration.
    
*   `<user-home>/.m2/develocity.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.

> [!NOTE]
> Be sure to include the XML namespace declarations to get auto-completion in your IDE. The latest version of the schema is available at https://www.gradle.com/schema/develocity-maven.xsd, or you can get a specific schema version by appending the Develocity Maven extension version to the schema location, e.g. https://www.gradle.com/schema/develocity-maven-2.4.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.

**develocity.xml:**

```
<develocity
  xmlns="https://www.gradle.com/develocity-maven" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://www.gradle.com/develocity-maven https://www.gradle.com/schema/develocity-maven.xsd">
  <!-- Whether the Develocity Maven extension should be enabled. Defaults to true. System property is 'develocity.enabled'. -->
  <enabled>true</enabled>
  <!-- Project identifier to be sent to the Develocity server when build scan is published.
       System property is 'develocity.projectId'. -->
  <projectId>myProject</projectId>
  <server>
    <!-- ID used to reference an element in the settings.xml.
         System property is 'develocity.serverId'. -->
    <id>my-server</id>
    <!-- Address of the Develocity server. System property is 'develocity.url'. -->
    <url>http://my-server/</url>
    <!-- Whether untrusted connections to the Develocity server should be accepted.
         Defaults to false. System property is 'develocity.allowUntrustedServer'. -->
    <allowUntrusted>false</allowUntrusted>
    <!-- The access key (without any hostname prefix) for authenticating with the Develocity server.
         Environment variable is 'DEVELOCITY_ACCESS_KEY'. -->
    <accessKey>7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq</accessKey>
    <!-- Whether Edge discovery is enabled. Enabling will use the remote build cache at the user's preferred location if ${develocity.buildCache.url} is not specified.
         Defaults to false. System property is 'develocity.edgeDiscovery'. -->
    <edgeDiscovery>false</edgeDiscovery>
  </server>
  <!-- Storage directory for caches and temporary data. Defaults to ${user.home}/.m2/.develocity.
       System property is 'develocity.storage.directory'. -->
  <storageDirectory>/some/location</storageDirectory>
  <buildScan>
    <!-- Allows configuring whether a build scan should be published at the end of the build. -->
    <publishing>
      <!-- Specify a condition for when a build scan should be published at the end of the build.
           You may add multiple such predicates. A build scan will be published if any of the
           predicates return true. -->
      <onlyIf><![CDATA[!buildResult.failures.empty && authenticated]]></onlyIf>
    </publishing>
    <!-- Terms of service acceptance (mandatory to publish to Develocity at gradle.com) -->
    <termsOfUse>
      <!-- Address of the terms of use. Must be 'https://gradle.com/help/legal-terms-of-use'.
           Defaults to an empty string. System property is 'develocity.scan.termsOfUse.url'. -->
      <url></url>
      <!-- Signal acceptance of the terms of use. Must be 'true'. Defaults to false.
           System property is 'develocity.scan.termsOfUse.accept'. -->
      <accept>false</accept>
    </termsOfUse>
    <!-- Whether to upload the build scan in background. Defaults to true.
         System property is 'develocity.scan.uploadInBackground' -->
    <backgroundBuildScanUpload>false</backgroundBuildScanUpload>
    <capture>
      <!-- Whether to capture content hashes of each input file for build scan comparison.
           Defaults to false. System property is 'develocity.scan.captureFileFingerprints' -->
      <fileFingerprints>true</fileFingerprints>
      <!-- Whether to capture build output for build scans.
           Defaults to true. System property is 'develocity.scan.captureBuildLogging' -->
      <buildLogging>true</buildLogging>
      <!-- Whether to capture test output for build scans.
           Defaults to true. System property is 'develocity.scan.captureTestLogging' -->
      <testLogging>true</testLogging>
      <!-- Whether to capture resource usage for build scans. Defaults to true. System property is 'develocity.scan.captureResourceUsage' -->
      <resourceUsage>true</resourceUsage>
    </capture>
    <!-- Obfuscated values for captured build scan data (optional). -->
    <obfuscation>
      <!-- The obfuscated username to capture (optional). -->
      <username></username>
      <!-- The obfuscated hostname to capture for local and public hostnames (optional). -->
      <hostname></hostname>
      <!-- The obfuscated IP addresses to capture (optional). -->
      <ipAddresses></ipAddresses>
      <!-- The obfuscated external process name to capture (optional). -->
      <externalProcessName></externalProcessName>
    </obfuscation>
    <!-- List of tags to capture.
         Additionally, system properties like 'scan.tag.<tag>' can be used to add tags. -->
    <tags>
      <tag>my tag</tag>
    </tags>
    <!-- List of links to capture.
         Additionally, system properties like 'scan.link.<name>=<url>' can be used to add links. -->
    <links>
      <link>
        <name>my link</name>
        <url>http://example.com</url>
      </link>
    </links>
    <!-- List of custom values to capture.
         Additionally, system properties like 'scan.value.<name>=<value>' can be used
         to add custom values. -->
    <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 'develocity.cache.local.enabled'. -->
      <enabled>true</enabled>
      <!-- Whether to store outputs in the local build cache (as opposed to only loading from it).
           Defaults to true. System property is 'develocity.cache.local.storeEnabled'. -->
      <storeEnabled>true</storeEnabled>
      <!-- Local cache directory. Defaults to ${user.home}/.m2/.develocity/build-cache.
           System property is 'develocity.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 'develocity.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 'develocity.cache.local.cleanup.retention'. -->
        <retention>P30D</retention>
        <!-- Interval at which the cleanup occurs.
             Defaults to P1D. System property is 'develocity.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 'develocity.cache.remote.serverId'. -->
        <id>remote-cache</id>
        <!-- URL of the remote cache. Defaults to ${develocity.url}/cache/.
             System property is 'develocity.cache.remote.url'. -->
        <url>http://my-node/cache/</url>
        <!-- Optionally specify the credentials.
             The credentials specified here take precedence over
             the credentials in your settings.xml -->
        <credentials>
          <!-- The username to use to connect to an authenticated cache node.
               System property is 'develocity.cache.remote.username'. -->
          <username>some-username</username>
          <!-- The password to use to connect to an authenticated cache node.
               System property is 'develocity.cache.remote.password'. -->
          <password>some-password</password>
        </credentials>
        <!-- Whether the remote cache accepts untrusted connections. Defaults to false.
             System property is 'develocity.cache.remote.allowUntrustedServer'. -->
        <allowUntrusted>true</allowUntrusted>
        <!-- Whether the client should enforce the use of HTTPS.
             Setting it to true will enable the usage of unencrypted HTTP.
             System property is 'develocity.cache.remote.allowInsecureProtocol'. -->
        <allowInsecureProtocol>true</allowInsecureProtocol>
        <!-- Whether the client should use HTTP Expect-Continue
             (https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3)
             when storing data on the server.
             System property is 'develocity.cache.remote.useExpectContinue'. -->
        <useExpectContinue>true</useExpectContinue>
      </server>
      <!-- Whether the remote cache is enabled.
           Defaults to true. System property is 'develocity.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 'develocity.cache.remote.storeEnabled'. -->
      <storeEnabled>true</storeEnabled>
    </remote>
    <!-- Whether the 'clean' lifecycle phase is required in order to store outputs in the build cache.
         Defaults to true. System property is 'develocity.cache.requireClean'. -->
    <!-- You should only set this to 'false' when the build is started from a clean working directory.
         Otherwise, produced build cache entries may be incorrect. -->
    <requireClean>true</requireClean>
  </buildCache>
</develocity>
```

<a id="expression_support"></a>

#### Expression Support

The `develocity.xml` configuration file supports two types of expressions that are evaluated when reading the configuration file:

**${…​}** — Maven-style expression (behaves like in pom.xml) for simple use cases, e.g. referencing an environment variable (e.g. `${env.CACHE_USERNAME}`)

**#{…​}** — [Spring Expression Language (SpEL)](https://docs.spring.io/spring-framework/reference/core/expressions.html) expressions for more complex use cases, e.g. converting an environment variable into a boolean (e.g. #{env\['CI'\] == null})

For both expression types, the following objects can be referenced:

**session** — the current `MavenSession` object (e.g. `${session.request.cacheNotFound}` or `#{session.request.cacheNotFound}`)

**basedir** — the base directory of the build (e.g. `${basedir/src}` or `#{basedir}/src`)

In addition, the following objects and functions can be referenced in SpEL expressions:

**properties: Properties** — user/system/profile properties (e.g. `#{properties['user.dir']}`). These are the same properties you can reference using the `${«property-name»}` syntax.  
User properties are passed via `-D` on the command-line. System properties are provided by the runtime and contain `System.getProperties()` as well as `env.`\-prefixed environment variables. Profile properties refer to properties defined in active Maven profiles.

**env: Map<String, String>** — environment variables (e.g. `#{env['CI']}`)

**username: String** — The username of the agent running the build (e.g. `#{username}`)

**ipAddresses: List<String>** — The IP addresses of the agent running the build (e.g. `#{ipAddresses}`)

**isTrue(Object): boolean** — returns `true` unless the supplied object’s `String` value case-insensitively equals `false`, `0`, `0.0`, or `null` (e.g. `#{isTrue(true)}`)

**isFalse(Object): boolean** — returns `true` if the supplied object’s `String` value case-insensitively equals `false`, `0`, `0.0`, or `null` (e.g. `#{isFalse(false)}`)

**sha512(Object): Object** — returns a SHA-512 String representation of the supplied object’s `String` value. If the supplied object is a `List`, returns a list of the individual `sha512` call for each item of the list.

The following example shows how to use both expression types to configure local and CI builds with a single `develocity.xml` file. It uses the `JENKINS_URL` environment variable (which is present in builds on Jenkins) to determine whether the build is running locally or on CI. Based on that, it enables the local Build Cache and background Build Scan upload only for local builds but enables writing to the remote Build Cache only for CI builds. Moreover, it determines the URL of the remote Build Cache based on the fictional `REGION` environment variable. Lastly, it uses Maven-style expressions to configure the remote Build Cache credentials based on custom environment variables that are typically injected by the CI server.

**develocity.xml:**

```
<develocity
  xmlns="https://www.gradle.com/develocity-maven" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://www.gradle.com/develocity-maven https://www.gradle.com/schema/develocity-maven.xsd">
  <buildScan>
    <backgroundBuildScanUpload>#{env['JENKINS_URL'] == null}</backgroundBuildScanUpload>
    <obfuscation>
      <username>#{sha512(username)}</username>
      <hostname>#{isTrue(env['CI']) ? 'CI agent' : 'Local agent'}</hostname>
      <ipAddresses>#{sha512(ipAddresses)}</ipAddresses>
    </obfuscation>
  </buildScan>
  <buildCache>
    <local>
      <enabled>#{env['JENKINS_URL'] == null}</enabled>
    </local>
    <remote>
      <server>
        <url>#{env['REGION'].startsWith('us') ? 'https://us.example.org/cache' : 'https://eu.example.org/cache'}</url>
        <credentials>
          <username>${env.DEVELOCITY_CACHE_USERNAME}</username>
          <password>${env.DEVELOCITY_CACHE_PASSWORD}</password>
        </credentials>
      </server>
      <enabled>true</enabled>
      <storeEnabled>#{env['JENKINS_URL'] != null}</storeEnabled>
    </remote>
  </buildCache>
</develocity>
```

<a id="pom"></a>

### pom.xml

The Develocity 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.

> [!NOTE]
> To get auto-completion in your IDE, be sure to include the XML namespace and schema location as shown in the example below. The latest version of the schema is always available at https://www.gradle.com/schema/develocity-maven-project.xsd, or you can get a specific schema version by appending the Develocity Maven extension version to the schema location, e.g. https://www.gradle.com/schema/develocity-maven-project-2.4.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.

**pom.xml:**

```
<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/develocity-maven-project https://www.gradle.com/schema/develocity-maven-project.xsd">

  <!-- other build configuration -->
  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>com.gradle</groupId>
          <artifactId>develocity-maven-extension</artifactId>
          <configuration>
            <develocity xmlns="https://www.gradle.com/develocity-maven-project">
              <normalization>
                <runtimeClassPath>
                  <ignoredFiles>
                    <ignoredFile>META-INF/build.properties</ignoredFile>
                  </ignoredFiles>
                  <propertiesNormalizations>
                    <propertiesNormalization>
                      <path>com/example/**/build.properties</path>
                      <ignoredProperties>
                        <ignore>build.timestamp</ignore>
                      </ignoredProperties>
                    </propertiesNormalization>
                  </propertiesNormalizations>
                  <metaInf>
                    <ignoreManifest>true</ignoreManifest>
                    <ignoreCompletely>true</ignoreCompletely>
                    <ignoredAttributes>
                      <ignore>Implementation-Version</ignore>
                    </ignoredAttributes>
                    <ignoredProperties>
                      <ignore>app.version</ignore>
                    </ignoredProperties>
                  </metaInf>
                </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>
                      <fileSet>
                        <name>config</name>
                        <paths>
                          <path>src/main/config</path>
                        </paths>
                        <normalization>
                          <strategy>RELATIVE_PATH</strategy>
                          <ignoreEmptyDirectories>true</ignoreEmptyDirectories>
                          <ignoreLineEndings>true</ignoreLineEndings>
                        </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>
                  <!-- Local state files are neither inputs, nor outputs. A typical example would be temporary directories.
                       They are deleted when the outputs of a goal execution are loaded from the cache. -->
                  <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>
                  </inputs>
                  <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>
                  <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://example.com</url>
                  </link>
                </links>
                <values>
                  <value>
                    <name>Build Number</name>
                    <value>${project.buildNumber}</value>
                  </value>
                </values>
              </buildScan>
            </develocity>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
```

<a id="custom_extension"></a>

### Programmatic Configuration

In order to access the Develocity Maven extension API to perform programmatic configuration of Build Scans and the Build Cache you need to create a [Maven extension](https://maven.apache.org/examples/maven-3-lifecycle-extensions.html). The [Common Custom User Data Maven Extension](https://github.com/gradle/common-custom-user-data-maven-extension) provided by Gradle Inc. provides an example. This extension can be applied directly to your project, or can serve as a template project for your own extension implementation.

See the [API reference](https://docs.gradle.com/downloads/maven-extension-javadoc/2.4.0/) for more details.

<a id="captured-information"></a>

## Appendix B: Captured Information

The Develocity 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.

<a id="listing"></a>

### Listing

The list below details the notable information captured by the Develocity Maven extension and transmitted in a Build Scan.

*   Environment
    
    *   User home (system property `'user.home'`)
        
    *   Maven home (system property `'maven.home'`)
        
    *   Username (system property `'user.name'`) (Can be [obfuscated](#obfuscating_identifying_data))
        
    *   Local hostname (environment variable `'COMPUTERNAME'` / `'HOSTNAME'`) (Can be [obfuscated](#obfuscating_identifying_data))
        
    *   Public hostname (Can be [obfuscated](#obfuscating_identifying_data))
        
    *   Local IP addresses (Can be [obfuscated](#obfuscating_identifying_data))
        
    *   <a id="toggler-jvm-content"></a>Build Java Virtual Machine ▶
        
        <a id="jvm-content"></a>
        
        *   Vendor (system property `'java.vendor'`)
            
        *   Runtime name (system property `'java.runtime.name'`)
            
        *   Runtime version (system property `'java.runtime.version'`)
            
        *   Version (system property `'java.version'`)
            
        *   Class version (system property `'java.class.version'`)
            
        *   VM information (system property `'java.vm.info'`)
            
        *   VM name (system property `'java.vm.name'`)
            
        *   VM vendor (system property `'java.vm.vendor'`)
            
        *   VM version (system property `'java.vm.version'`)
            
        *   <a id="toggler-jvm-locale-content"></a>Locale ▶
            
            <a id="jvm-locale-content"></a>
            
            *   Language
                
            *   Country
                
            *   Variant
                
            *   Timezone
                
            
        *   Memory utilization and settings
            
        
    *   <a id="toggler-os-content"></a>Operating System ▶
        
        <a id="os-content"></a>
        
        *   Name (system property `'os.name'`)
            
        *   Version (system property `'os.version'`)
            
        *   Architecture (system property `'os.arch'`)
            
        *   Process names (Some can be [obfuscated](#obfuscating_identifying_data))
            
        *   CPU load of the build process
            
        *   CPU load of the build descendant processes
            
        *   Memory allocated to the build process
            
        *   Memory allocated to the build descendant processes
            
        
    *   <a id="toggler-hardware-content"></a>Hardware ▶
        
        <a id="hardware-content"></a>
        
        *   Number of processors
            
        *   CPU load of the system
            
        *   Total memory used by the system
            
        *   Network download/upload throughput across all non-local interfaces
            
        *   Disk read/write throughput across all disks
            
        
    *   <a id="toggler-git-content"></a>Git ▶ \[<a id="_footnoteref_1"></a>[1](#_footnotedef_1 "View footnote.")\]
        
        <a id="git-content"></a>
        
        *   Git repository path
            
        *   Committer and author email addresses
            
        *   Timestamps of commits
            
        
    
*   Build
    
    *   Build invocation options (e.g. requested phases and goals, switches)
        
    *   Build console output
        
    *   Build failure exception messages and stack traces
        
    *   <a id="toggler-projects-content"></a>Projects and structure ▶
        
        <a id="projects-content"></a>
        
        *   Group ID
            
        *   Artifact ID
            
        *   Packaging
            
        *   Version number
            
        *   Disk location
            
        
    *   <a id="toggler-executing-goals-content"></a>Executed goals ▶
        
        <a id="executing-goals-content"></a>
        
        *   Goal name
            
        *   Goal execution outcome
            
        *   Goal execution ID
            
        *   Lifecycle phase
            
        *   Reason for a non-cacheable goal
            
        *   Plugin applying the goal
            
        *   Mojo classname
            
        *   Build Cache operations, including cache key
            
        *   Goal input and output properties (excluding their values)
            
        
    *   <a id="toggler-build-tests-content"></a>Executed tests (using Apache Maven Surefire plugin) ▶
        
        <a id="build-tests-content"></a>
        
        *   Configuration of a test goal (e.g. number of parallel forks)
            
        *   Test names (e.g. class and method name) and outcome
            
        *   Console output
            
        *   Failure exception messages and stack traces
            
        *   Names and local hostnames of Test Distribution agents on which the tests ran
            
        *   Paths and sizes of files transferred to and from remote Test Distribution agents
            
        
    *   <a id="toggler-build-dependencies-content"></a>Resolved dependencies ▶
        
        <a id="build-dependencies-content"></a>
        
        *   Identifiers and versions (e.g. `javax.inject:javax.inject:1`)
            
        *   Resolution failure error messages
            
        *   Origin repositories
            
        *   Source (resolved locally or downloaded from remote repository)
            
        *   Artifact size
            
        
    *   <a id="toggler-build-extensions-content"></a>Applied extensions ▶
        
        <a id="build-extensions-content"></a>
        
        *   Group ID
            
        *   Artifact ID
            
        *   Version number
            
        *   Disk location
            
        *   Class realm
            
        *   Listener classname
            
        *   Listener role
            
        
    *   <a id="toggler-build-plugins-content"></a>Applied plugins ▶
        
        <a id="build-plugins-content"></a>
        
        *   Group ID
            
        *   Artifact ID
            
        *   Version number
            
        *   Goal prefix
            
        *   Required Maven version
            
        
    *   <a id="toggler-build-downloads-content"></a>Network downloads (performed by Maven) ▶
        
        <a id="build-downloads-content"></a>
        
        *   Download time
            
        *   Download size
            
        *   Download location (including repository location)
            
        
    *   Build Cache configuration
        
    *   Background Build Scan publication
        
    *   Maven Build Cache extension cache report
        
    *   Maven Daemon enablement
        
    

<a id="access"></a>

### Access

Build Scans published to a Develocity installation are viewable by all users that can reach the server and have the required roles, should Identity Access Management (IAM) be turned on. Develocity provides a search interface for discovering and finding individual Build Scans.

Build Scans published to [Develocity at gradle.com](https://gradle.com/scans/gradle/) are viewable by anyone with the link assigned when publishing the Build Scan. Links to individual Build Scans aren’t discoverable and cannot be guessed, but may be shared.

<a id="cacheable_plugins_and_goals"></a>

## Appendix C: 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.

More Build Cache configurations for additional Maven plugins, which aren’t supported out of the box by Develocity, can be viewed [here](https://github.com/gradle/develocity-build-config-samples/tree/main/build-caching-maven-samples).

<a id="maven_compiler_plugin"></a>

### 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 Build 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
    

<a id="compile_avoidance"></a>

#### Compile Avoidance

Unless there are annotation processors on the classpath, the extension uses [compile avoidance](https://docs.gradle.com/develocity/maven/2.4/build-cache-maven/#compile_avoidance) so your sources are only recompiled if the signatures of the classes on the compile classpath have changed.

<a id="maven-surefire-plugin-and-maven-failsafe-plugin"></a>

### maven-surefire-plugin and maven-failsafe-plugin

Supported versions: 2.12.4 and above for Build Cache, 2.15 (except for 2.19 to 2.20.1) and above for Build Scans, 2.22.2 and above for Test Distribution and Predictive Test Selection.

Supported goals:

*   `surefire:test`
    
*   `failsafe:integration-test`
    

Caching is automatically disabled if:

*   [`<debugForkedProcess>`](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#debugForkedProcess) is true
    
*   [`<workingDirectory>`](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#workingDirectory) is changed to something different from the project’s root dir
    

> [!NOTE]
> Test results for `surefire:test` are stored in the Build Cache whenever the goal succeeds. Thus, by default, only successful or skipped test results are cached. However, if [`<testFailureIgnore>`](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#testFailureIgnore) is set to `true`, test failures are cached as well.

The following use cases currently require disabling the Build Cache for these plugins:

*   non-deterministic tests (e.g. tests with random parameters)
    
*   Tests that read files that aren’t 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 aren’t explicitly declared using the [`<environmentVariables>`](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#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 shouldn’t influence the test result:

*   All concurrency settings, e.g. [`<threadCount>`](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#threadCount)
    
*   User properties (passed via `-D` to Maven) that aren’t declared in the goal configuration using `<systemPropertyVariables>`, `<systemProperties>`, or `<argLine>`.
    

If a user property (e.g. `-Dmy.custom.property=someValue`) influences the outcome or behavior of tests, it should be tracked as an input by declaring it explicitly:

**pom.xml:**

```
<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <systemPropertyVariables>
      <my.custom.property>${my.custom.property}</my.custom.property>
    </systemPropertyVariables>
  </configuration>
</plugin>
```

<a id="jacoco-maven-plugin"></a>

### 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
    

The JaCoCo plugin hooks into surefire and failsafe as a Java agent, or can preemptively instrument classes when used in 'offline' mode. The extension automatically tracks all JaCoCo agent options and the `jacoco-agent.destfile` system property 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.

Caching is automatically disabled if:

*   [`<append>`](https://www.eclemma.org/jacoco/trunk/doc/prepare-agent-mojo.html#append) is true
    

To allow tests to be cached while using JaCoCo, have each surefire/failsafe execution write to a separate data file and use a [`jacoco:merge`](https://www.eclemma.org/jacoco/trunk/doc/merge-mojo.html) or [`jacoco:report-aggregate`](https://www.eclemma.org/jacoco/trunk/doc/report-aggregate-mojo.html) goal to create a merged data file or an aggregated report.

<a id="maven-jaxb2-plugin"></a>

### maven-jaxb2-plugin/jaxb-maven-plugin (org.jvnet.jaxb2.maven2/org.jvnet.jaxb)

Supported plugin artifact ids:

*   `maven-jaxb2-plugin`
    
*   `maven-jaxb20-plugin`
    
*   `maven-jaxb21-plugin`
    
*   `maven-jaxb22-plugin`
    
*   `maven-jaxb23-plugin`
    
*   `jaxb-maven-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 Build 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 shouldn’t 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`)
    

<a id="maven-javadoc-plugin"></a>

### 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>`](https://maven.apache.org/plugins/maven-javadoc-plugin/javadoc-mojo.html#skip) is true
    

The following properties are deliberately not tracked as inputs, because they should not influence the javadoc output:

*   [`<quiet>`](https://maven.apache.org/plugins/maven-javadoc-plugin/javadoc-mojo.html#quiet)
    
*   [`<serialwarn>`](https://maven.apache.org/plugins/maven-javadoc-plugin/javadoc-mojo.html#serialwarn)
    
*   [`<verbose>`](https://maven.apache.org/plugins/maven-javadoc-plugin/javadoc-mojo.html#verbose)
    

<a id="maven-checkstyle-plugin"></a>

### maven-checkstyle-plugin

Supported versions: 2.14 and above

Supported goals:

*   `check`
    
*   `checkstyle`
    
*   `checkstyle-aggregate`
    

Caching is automatically disabled if:

*   [`<skip>`](https://maven.apache.org/plugins/maven-checkstyle-plugin/check-mojo.html#skip) is true
    

The following properties are deliberately not tracked as inputs, because they don’t influence the outcome of the goal:

*   [`<consoleOutput>`](https://maven.apache.org/plugins/maven-checkstyle-plugin/check-mojo.html#consoleOutput)
    
*   [`<logViolationsToConsole>`](https://maven.apache.org/plugins/maven-checkstyle-plugin/check-mojo.html#logViolationsToConsole)
    

> [!NOTE]
> 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’s executing the build. In case that’s problematic for you, please disable the Build Cache for this goal.

<a id="tycho-maven-plugin"></a>

### tycho-maven-plugin

Supported versions: 4.0.7 and above

Supported goals:

*   `test`
    
*   `plugin-test`
    

Caching is automatically disabled if:

*   [test `<dependencies>`](https://tycho.eclipseprojects.io/doc/main/tycho-surefire-plugin/test-mojo.html#dependencies) isn’t empty
    
*   [plugin-test `<dependencies>`](https://tycho.eclipseprojects.io/doc/main/tycho-surefire-plugin/plugin-test-mojo.html#dependencies) isn’t empty
    

This is intended because `<dependencies>` contains only OSGI coordinates that would be costly to resolve during cache key computation.

<a id="anatomy-of-the-develocity-directory"></a>

## Appendix D: Anatomy of the .develocity Directory

By default, the Develocity Maven extension stores temporary data in the `${user.home}/.m2/.develocity` directory. If you want to customize this location, use one of the following options:

<a id="tabs-59"></a>

*   <a id="tabs-59-xml"></a>
    
    XML
    
*   <a id="tabs-59-java"></a>
    
    Java
    
*   <a id="tabs-59-shell"></a>
    
    Shell
    

<a id="tabs-59-xml--panel"></a>

**Add the following to develocity.xml:**

```
<develocity>
  <storageDirectory>/path/to/new/storage/directory</storageDirectory>
</develocity>
```

<a id="tabs-59-java--panel"></a>

**Using the Programmatic Configuration:**

```
develocity.setStorageDirectory(java.nio.file.Paths.get("/path/to/new/storage/directory"));
```

<a id="tabs-59-shell--panel"></a>

```shell
mvn clean verify -Ddevelocity.storage.directory=path/to/new/storage/directory
```

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 a Build Scan

**fingerprint-cache** — Location of the local fingerprint cache

**token-cache** — Location of cached entitlement tokens

> [!WARNING]
> The .develocity directory is an internal directory and subject to change without warning.

<a id="compatibility-with-apache-maven-and-develocity"></a>

## Appendix E: Compatibility with Apache Maven and Develocity

Compatibility between versions of Apache Maven, Develocity, and the Develocity Maven extension can be found in the [Develocity compatibility document](https://docs.gradle.com/develocity/2026.1/miscellaneous/compatibility/).

<a id="compatibility-with-apache-maven-plugins"></a>

## Appendix F: Compatibility with Apache Maven Plugins

Certain versions of the Apache Maven plugins listed below are incompatible with the Develocity Maven extension. If using one of those plugins, please ensure to use a compatible version.

  
| Plugin | Compatible versions | Reason |
| --- | --- | --- |
| Maven Artifactory Plugin | 3.1.0+ | Previous versions trigger unexpected Project started/Project finished callbacks |

<a id="compatibility-with-intellij-idea"></a>

## Appendix G: Compatibility with IntelliJ IDEA

No Build Scan data will be published and the Build Cache won’t be used when running builds from IDEA versions below 2019.2 or version 2019.3. This is due to issues in these IDEA versions that we can’t work around. We recommend using IDEA 2019.3.1 or above to get the best experience.

<a id="verifying-the-signature-of-the-extension-jar"></a>

## Appendix H: Verifying the Signature of the Extension JAR

The extension jar is published to Maven Central alongside its signature (cf. [OSSRH Guide](https://central.sonatype.org/publish/requirements/gpg/)). The public key is published to [https://keys.openpgp.org](https://keys.openpgp.org). You can verify the signature as follows:

```shell
curl -OL https://repo1.maven.org/maven2/com/gradle/develocity-maven-extension/2.4.0/develocity-maven-extension-2.4.0.jar && \
  curl -OL https://repo1.maven.org/maven2/com/gradle/develocity-maven-extension/2.4.0/develocity-maven-extension-2.4.0.jar.asc && \
  gpg --keyserver keys.openpgp.org --recv-key  7B79ADD11F8A779FE90FD3D0893A028475557671 && \
  gpg --verify develocity-maven-extension-2.4.0.jar.asc develocity-maven-extension-2.4.0.jar
```

The output of the last command should look similar to the following:

**Output:**

```
gpg: Signature made Thu Sep 28 16:17:46 2023 CEST
gpg:                using RSA key 893A028475557671
gpg: Good signature from "Gradle Inc. <info@gradle.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7B79 ADD1 1F8A 779F E90F  D3D0 893A 0284 7555 7671
```

This verifies that the artifact was signed with the private key that corresponds to the imported public key. The warning is emitted because you haven’t explicitly trusted the imported key (therefore `[unknown]`). One way of establishing trust is to verify the fingerprint over a secure channel. Please contact technical support should you want to do so.

> [!NOTE]
> The access key used to sign older versions of Develocity Maven extension is revoked. Verifying the signature of these prior versions is no longer possible.

<a id="running-goals"></a>

## Appendix I: Running Goals Contributed by the Develocity Maven Extension

The `com.gradle` plugin group exposes the `develocity` prefix in [Maven Central](https://repo.maven.apache.org/maven2/com/gradle), which allows to execute goals exposed by the Develocity Maven extension with a shorter syntax.

*   When using Develocity Maven extension version 1.16 or above, the `com.gradle` plugin group is automatically added to the Maven execution request, so no configuration is needed.
    
*   When using Develocity Maven extension version < 1.16, you must instruct Maven to search for goal prefixes within the `com.gradle` plugin group. You must add the following code snippet to your `~/.m2/settings.xml` or `${maven.home}/conf/settings.xml`.
    

```xml
<pluginGroups>
  <pluginGroup>com.gradle</pluginGroup>
</pluginGroups>
```

Then, instead of executing

```shell
mvn com.gradle:develocity-maven-extension:<<goal-name>>
```

you can execute

```shell
mvn develocity:<<goal-name>>
```

> [!NOTE]
> When omitting the Develocity Maven extension version, Maven will select the latest version available. You can force using a given version by executing but this doesn’t work with the goal prefix syntax, unless you declare the extension in your pom.xml.

<a id="goals-provided-by-the-develocity-maven-extension"></a>

### Goals Provided by the Develocity Maven Extension

The following goals are exposed by the Develocity Maven extension.

<a id="build-scan-publish-previous"></a>

#### build-scan-publish-previous

Publishes a Build Scan for the most recently run build.

<a id="provision-access-key"></a>

#### provision-access-key

Authenticates your build environment with Develocity.

<a id="init"></a>

#### init

Sets up the Develocity Maven extension in a Maven project. Without applying the Develocity Maven extension in the first place, you can execute:

```shell
mvn com.gradle:develocity-maven-extension:2.4.0:init -Ddevelocity.url=https://develocity.example.com
```

It will:

*   Create/update the `.mvn/extensions.xml` with the given Develocity Maven extension release version:
    

```shell
mvn com.gradle:develocity-maven-extension:2.4.0:init
```

*   Create/update the `.mvn/extensions.xml` with the Develocity Maven extension
    
*   Create the `.mvn/develocity.xml` pointing to the passed server
    
*   Create/update `.gitignore` to ignore `.mvn/.develocity/`
    

<a id="help"></a>

#### help

Displays help information on the goals provided by the extension.

<a id="footnotes"></a>

* * *

<a id="_footnotedef_1"></a>[1](#_footnoteref_1). Not captured when publishing to [our free Build Scan hosting service](https://gradle.com/scans/maven/) or any of our [sponsored open source instances](https://gradle.com/oss-sponsored-by-develocity/).