---
component: sbt
version: "1.4"
slug: sbt/sbt-plugin
canonical_url: "https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/"
title: "Develocity sbt Plugin User Manual"
description: "User manual for the Develocity sbt plugin."
keywords: []
status: current
---

> Canonical: <https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/>

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

# Develocity sbt Plugin User Manual

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

The Develocity sbt plugin enables integration with [Develocity](https://gradle.com) and [gradle.com/scans/sbt/](https://gradle.com/scans/sbt/).

<a id="getting-set-up"></a>

## Getting Set Up

The latest version of the Develocity sbt plugin is 1.4.5 and is compatible with sbt 1.9.0 and all versions above.

The instructions in this section describe applying and configuring the plugin for a single sbt project.

> [!NOTE]
> If you wish to use Develocity with older sbt versions, please contact your customer success representative.

<a id="applying-the-plugin"></a>

### Applying the Plugin

Apply the Develocity sbt plugin to your build by adding the following configuration block to a new or existing `project/plugins.sbt` file in your sbt project. The sbt plugin will be downloaded automatically from [Maven Central](https://central.sonatype.com/search?q=a:sbt-develocity_2.12_1.0) once you load your build.

**project/plugins.sbt:**

```
addSbtPlugin("com.gradle" % "sbt-develocity" % "1.4.5")
```

<a id="connecting_to_develocity"></a>

### Connecting to Develocity

All Develocity settings are grouped under one configuration object that’s recommended to be set in the [`ThisBuild` scope](https://www.scala-sbt.org/1.x/docs/Scopes.html#Build-level+settings). Unless you intend to publish Build Scans to [Develocity at gradle.com](https://gradle.com/scans/gradle/), the minimum configuration required is that of the Develocity server URL.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withServer(
      previous.server
        .withUrl(url("https://develocity.example.com"))
    )
}
```

Other configuration options will be described as we introduce each feature, but you can find all configuration options in the [configuration reference](#develocityconfiguration).

<a id="allowing-untrusted-ssl-communication"></a>

#### Allowing Untrusted SSL Communication

If your Develocity server uses an SSL certificate that isn’t trusted by your build’s Java runtime, you won’t be able to publish Build Scans without explicitly allowing untrusted servers.

To do this, set the `allowUntrusted` option to `true`:

**Disabling SSL certificate checks:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withServer(
      previous.server
        .withUrl(url("https://develocity.example.com"))
        .withAllowUntrusted(true)
    )
}
```

Use of this configuration is a security risk as it makes it easier for a third party to intercept your Build Scan data. It should only be used as a short term workaround until the server can be configured with a trusted certificate.

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

#### Authenticating

If your instance doesn’t require authentication, you can skip to [Using Build Scan](#using_build_scan)

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 `develocityProvisionAccessKey` task.

```shell
sbt develocityProvisionAccessKey
```

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 `keys.properties` file within the Develocity storage directory (`~/.sbt/1.0/.develocity` 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 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, environment variable or the settings file.

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

###### Via File

Develocity access keys are stored inside the Develocity storage directory user home directory (`~/.sbt/1.0/.develocity` by default), at `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 && \
  sbt compile
```

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 && \
  sbt compile
```

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

###### Via Configuration

You can also specify the access key in your build file:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withServer(
      previous.server
        .withUrl(url("https://develocity.example.com"))
        .withAccessKey("7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq")
    )
}
```

<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="connecting-to-scans-gradle-com"></a>

### Connecting to Develocity at gradle.com

A free version of Develocity is available at [Develocity at gradle.com](https://gradle.com/scans/gradle/), but before you can publish a Build Scan you need to agree to the terms of use, which can be found at [https://gradle.com/legal/terms-of-use/](https://gradle.com/legal/terms-of-use/).

You can agree to the terms of use by adding the following configuration to the build:

**Agreeing to the terms of use:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withTermsOfUse(url("https://gradle.com/legal/terms-of-use/") -> true)
    )
}
```

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

> [!WARNING]
> Be careful not to commit agreement to the terms of use into a project that may be built by others.

<a id="using_build_scan"></a>

## Using Build Scan

Build Scan® is a shareable record of what happened during a build, captured and visualized by Develocity.

<a id="what-is-part-of-a-single-build-scan"></a>

### What Is Part of a Single Build Scan?

Unlike other build tools, sbt is often used by typing commands in its own interactive shell. In this mode, sbt will publish a Build Scan for each top-level task that it runs.

<a id="interactive-mode"></a>

#### Interactive Mode

For example, when invoking a single command in the shell:

```shell
sbt:test-project> compile
```

**Output:**

```
[info] compiling 1 Scala source to test-project/core/target/scala-2.13/classes ...
[info] compiling 1 Scala source to test-project/backend/target/scala-2.13/classes ..
[info] Publishing Build Scan to Develocity...
[info] https://develocity.example.com/s/ohd4cybh6yc4g
```

sbt allows one to chain several commands separated by semicolons, as in `compile; test`. In this case, a Build Scan is published for each one of them:

```shell
sbt:test-project> compile; test
```

**Output:**

```
[info] compiling 1 Scala source to test-project/core/target/scala-2.13/classes ...
[info] compiling 1 Scala source to test-project/backend/target/scala-2.13/classes ...
[success] Total time: 1 s, completed 23 Jun 2023, 15:16:51
[info] Publishing Build Scan to Develocity...
[info] https://develocity.example.com/s/lmzvnpyy66vbw
[info] compiling 2 Scala sources to test-project/core/target/scala-2.13/test-classes ...
...
[info] Publishing Build Scan to Develocity...
[info] https://develocity.example.com/s/pg37gbjxr4sxo
```

<a id="batch-mode"></a>

#### Batch Mode

When sbt is invoked with several tasks on the command line, as it’s often the case on CI, sbt will publish a single Build Scan for the whole sbt process.

```shell
sbt compile test
```

**Output:**

```
[info] welcome to sbt 1.9.0 (BellSoft Java 17.0.4)
[info] loading global plugins from /../.sbt/1.0/plugins
...
[info] Publishing Build Scan to Develocity...
[info] https://develocity.example.com/s/5563ccpas77ua
```

<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 sbt plugin 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

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.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withPublishing(Publishing.onlyIf(_ => true))
    )
}
```

> [!NOTE]
> If you want to deactivate Build Scans for a particular build, you can pass the -Dscan=false system property to sbt.

<a id="publish_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 publish configuration:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withPublishing(Publishing.onlyIf(_ => false))
    )
}
```

When publishing on-demand, you can pass the `scan` system property to control publishing:

```shell
sbt -Dscan
```

This system property overrides the configured value, so it can also be used to disable publishing, regardless of the build setting by setting it to `false`.

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

**Limit publishing of Build Scans on failure:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  val onlyOnFailure = Publishing.onlyIf { ctx => ctx.buildResult.failures.nonEmpty }
  previous
    .withBuildScan(
      previous.buildScan
        .withPublishing(onlyOnFailure)
    )
}
```

**Limit publishing of Build Scans to authenticated users:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  val onlyIfAuthenticated = Publishing.onlyIf { ctx => ctx.authenticated }
  previous
    .withBuildScan(
      previous.buildScan
        .withPublishing(onlyIfAuthenticated)
    )
}
```

**Restricting Build Scans to CI builds:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  val onlyOnCi = Publishing.onlyIf { _ => sys.env.contains("CI") }
  previous
    .withBuildScan(
      previous.buildScan
        .withPublishing(onlyOnCi)
    )
}
```

<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 (for example, 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.

<a id="disabling-programmatically"></a>

#### Disabling Programmatically

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withBackgroundUpload(false)
    )
}
```

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

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withBackgroundUpload(!sys.env.get("CI").exists(_.toBoolean))
    )
}
```

<a id="disabling-via-system-property"></a>

#### Disabling via System Property

Background uploading can be disabled by setting the `develocity.scan.uploadInBackground` system property to `false`. The system property setting always takes precedence over the programmatic setting.

```shell
sbt -Ddevelocity.scan.uploadInBackground=false compile test publish
```

<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 0.10 of this plugin don’t allow specifying project identifier

<a id="configuring-programmatically"></a>

#### Configuring programmatically

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withProjectId(ProjectId("myProject"))
}
```

<a id="configuring-via-system-property"></a>

#### Configuring via System Property

Project identifier can be specified by setting the `develocity.projectId`. The system property setting always takes precedence over the programmatic setting.

```shell
sbt -Ddevelocity.projectId=myProject compile test publish
```

<a id="capturing-build-output"></a>

### Capturing Build Output

By default, console log messages generated during the build are captured and displayed in Build Scans.

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

#### When to Disable

You may want to skip capturing console log messages for security and privacy reasons, such as console log messages that leak sensitive data, or performance and storage reasons, such as tasks that produce many console log messages that are irrelevant for your use of Build Scans.

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

#### How to Disable

Console log messages capture can be disabled programmatically via the Develocity configuration object, or via a system property.

<a id="programmatically"></a>

##### Programmatically

To disable programmatically, set the `buildLogging` option to `false`:

**Disabling build output capturing:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withCapture(
          previous.buildScan.capture
            .withBuildLogging(false)
        )
    )
}
```

<a id="via-system-property"></a>

##### Via System Property

To disable without modifying the build script, supply the `develocity.scan.captureBuildLogging` system property to the build. If the property is set to `false`, capture is disabled. Otherwise, capture is enabled. The system property setting always takes precedence over the programmatic setting.

```shell
sbt -Ddevelocity.scan.captureBuildLogging=false compile test publish
```

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

### Capturing Resource Usage

_(sbt plugin 1.1+)_

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.

> [!WARNING]
> The minimum sbt version required to capture usage insights is 1.7.0. When using the Develocity sbt plugin 1.1+ with earlier versions of sbt, resource usage capturing will be automatically disabled, and a warning message will be printed.

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

#### When to Disable

You may want to skip capturing resource usage for security/privacy reasons, or because you are unable to upgrade to sbt 1.7.0+. 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-2"></a>

#### How to Disable

Resource usage capture can be disabled programmatically, or via a system property.

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

##### Programmatically

To disable programmatically, create a copy of the original `capture` configuration with an updated behavior regarding resource usage capturing:

**Disabling resource usage capturing:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withCapture(
          previous.buildScan.capture
            .withResourceUsage(false)
        )
    )
}
```

<a id="via-system-property-2"></a>

##### Via System Property

To disable without modifying the build script, supply the `develocity.scan.captureResourceUsage` system property to the build. If the property is set to `false`, capture is disabled. Otherwise, capture is enabled. The system property setting always takes precedence over the programmatic setting.

```shell
sbt -Ddevelocity.scan.captureResourceUsage=false compile test publish
```

<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 1 and 2:

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

![A Build Scan Containing Tags and Links](https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/../_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/sbt/1.4/sbt-plugin/../_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 3, for example, we’re filtering for all Build Scans that have the tag "CI" and a git branch name of "main":

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

![A Filtered List of Build Scans in Develocity](https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/../_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.

They can be added at build time via the `buildScan.withTags` (to replace all tags) or `buildScan.tag` (to add a single tag) utility methods:

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

*   <a id="tabs-1-scala"></a>
    
    Scala
    
*   <a id="tabs-1-shell"></a>
    
    Shell
    

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

**Adding tags to a build’s Build Scans:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .tag(if (sys.env.contains("CI")) "CI" else "Local")
        .tag(sys.props("os.name"))
    )
}
```

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

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

```
sbt -Dscan.tag.CI -Dscan.tag.$(uname -o)
```

As demonstrated by the example above, tags are typically applied either as fixed strings within a condition or evaluated at runtime from the environment. But there are no set rules that you need to follow—these are only suggestions.

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.

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

They can be added at build time via the `buildScan.withLinks` (to replace all links) or `buildScan.link` (to add a single link) utility methods:

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

*   <a id="tabs-2-scala"></a>
    
    Scala
    
*   <a id="tabs-2-shell"></a>
    
    Shell
    

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

**Adding a VCS URL to a build’s Build Scans:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .link("VCS", url(s"https://github.com/myorg/sample/tree/${sys.props("vcs.branch")}"))
    )
}
```

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

**Adding a link via the command line:**

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

The above example demonstrates how you can attach a link to an online VCS repository that points to a specific branch provided as a system property.

Links are Scala pairs, with the first field being the label, and the second one a valid URL. The <label> 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 1](#img-scan-with-custom-data-1), which shows how a label _Source_ becomes a hyperlink that anyone viewing the Build Scan can follow.

> [!NOTE]
> There are 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-3"></a>

*   <a id="tabs-3-scala"></a>
    
    Scala
    
*   <a id="tabs-3-shell"></a>
    
    Shell
    

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

**Adding custom values to a build’s Build Scans:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .value("Scala version", scalaVersion.value)
    )
}
```

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

**Adding custom values via the command line:**

```
sbt "-Dscan.value.Git branch=$(git branch --show-current)"
```

This examples shows that you can include any sbt setting value. This applies to tags and links as well.

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

> [!NOTE]
> There are 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="switches"></a>

### Capturing Settings and Configuration Options

Develocity sbt plugin captures the state of selected boolean settings and configuration options. These settings and configuration options can have a significant impact on the behavior of the entire build. Monitoring them provides insights into the overall build configuration. The state of the captured settings and configuration options is available via the Build Scan.

All settings are captured at [`ThisBuild` scope](https://www.scala-sbt.org/1.x/docs/Scopes.html#Build-level+settings).

<a id="switches_background_build_scan_publication"></a>

#### Background Build Scan Publication

Indicates whether the build was configured to upload the Build Scan in the background.

Information about how to configure background uploading for Build Scans can be found in [Configuring background uploading](#configuring_background_uploading) section.

<a id="switches_build_output_capturing"></a>

#### Build Output Capturing

Indicates whether the console log messages generated during the build are captured and displayed in Build Scans.

Information about how to configure build output capturing for Build Scans can be found in [Capturing build output](#capturing-build-output) section.

<a id="switches_use_coursier"></a>

#### Use Coursier

Indicates whether the build was configured to use [Coursier](https://get-coursier.io/) for dependencies resolution.

Coursier is the default dependency resolution library since [sbt 1.3.0](https://www.scala-sbt.org/1.x/docs/sbt-1.3-Release-Notes.html#Library+management+with+Coursier)

<a id="switches_offline"></a>

#### Offline

Indicates whether the sbt was configured to work without a network connection where possible.

<a id="switches_turbo"></a>

#### Turbo

Indicates whether the build was configured to use optional performance optimization features.

Turbo mode is available since [sbt 1.3.0](https://www.scala-sbt.org/1.x/docs/sbt-1.3-Release-Notes.html#Turbo+mode+with+ClassLoader+layering).

<a id="switches_parallel_execution"></a>

#### Parallel Execution

Indicates whether the build was configured to use parallel tasks execution.

Information about parallel tasks execution can be found in the [reference documentation](https://www.scala-sbt.org/1.x/docs/Parallel-Execution.html#Parallel+Execution).

<a id="switches_semanticdb_enabled"></a>

#### SemanticDB Scalac Plugin

Indicates whether the projects in the build were configured to use SemanticDB Scalac plugin.

SemanticDB support is available since [sbt 1.3.0](https://www.scala-sbt.org/1.x/docs/sbt-1.3-Release-Notes.html#SemanticDB+support).

<a id="switches_interactive_mode"></a>

#### Interactive Mode

Indicates whether the build was run from within the sbt shell, or in batch mode.

Information about the sbt interactive mode can be found in the [reference documentation](https://www.scala-sbt.org/1.x/docs/Howto-Interactive-Mode.html#Interactive+mode).

<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’s not decipherable in Build Scans when viewed, by registering obfuscation functions as part of the sbt plugin configuration.

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

Obfuscating the username:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withObfuscation(
          previous.buildScan.obfuscation
            .withUsername(_.reverse)
        )
    )
}
```

Obfuscating the hostnames:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withObfuscation(
          previous.buildScan.obfuscation
            .withHostname(_.toCharArray.map(_.getNumericValue).mkString("-"))
        )
    )
}
```

Obfuscating the IP addresses:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withObfuscation(
          previous.buildScan.obfuscation
            .withIpAddresses(_.map(_ => "0.0.0.0"))
        )
    )
}
```

Obfuscating the non build related process names (_sbt plugin 1.1+_):

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withObfuscation(
          previous.buildScan.obfuscation
            .withExternalProcessName(_ => "non-build-process")
        )
    )
}
```

<a id="capturing-test-data-in-a-custom-configuration"></a>

### Capturing Test Data in a Custom Configuration

sbt supports user-defined [custom configuration](https://www.scala-sbt.org/1.x/docs/Advanced-Configurations-Example.html). By default, the Develocity sbt plugin sets up the `Test` and `IntegrationTest` configurations to capture test data.

It’s possible to configure the Develocity sbt plugin to capture test data produced in custom configurations as well:

**build.sbt:**

```
// The configuration for which you want to capture test data
lazy val MyCustomConfig = config("Custom").extend(Test)

lazy val myProject = project(...).settings(
  // Add the Develocity test settings in your custom configuration:
  inConfig(MyCustomConfig)(DevelocityPlugin.testSettings)
)
```

<a id="using_the_build_cache"></a>

## Using the Build Cache

_(sbt plugin 1.1+)_

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 task 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 task 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/2026.1/using-develocity/build-cache/) 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 sbt 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

To use build caching for sbt, you need to configure the location of your Develocity server.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withServer(
      previous.server
        .withUrl(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`:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withServer(
      previous.server
        .withUrl(url("https://develocity.example.com"))
        .withAllowUntrusted(true)
    )
}
```

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

#### Configuring the Local Build Cache

The plugin 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. The local Build Cache can be disabled by providing a `LocalBuildCache` where the `enabled` field is set to `false`:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withLocal(
          previous.buildCache.local
            .withEnabled(false)
        )
    )
}
```

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

##### Disabling Local Store

By default, outputs of a given task are stored in the local Build Cache if it’s enabled and the scope of the task is clean. Storing outputs in the local Build Cache can be disabled explicitly by setting the `storeEnabled` option to `false`.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withLocal(
          previous.buildCache.local
            .withStoreEnabled(false)
        )
    )
}
```

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

##### Changing the Local Build Cache Directory

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

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withLocal(
          previous.buildCache.local
            .withDirectory(file("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`.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withLocal(
          previous.buildCache.local
            .withCleanup(
              previous.buildCache.local.cleanup
                .withEnabled(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`.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withLocal(
          previous.buildCache.local
            .withCleanup(
              previous.buildCache.local.cleanup
                .withRetention(java.time.Duration.ofDays(30))
                .withInterval(java.time.Duration.ofDays(10))
            )
        )
    )
}
```

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

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withRemote(
          previous.buildCache.remote
            .withServer(
              previous.buildCache.remote.server
                .withUrl(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:

> [!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

The same access key credential used for Build Scan publishing is 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.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withRemote(
          previous.buildCache.remote
            .withServer(
              previous.buildCache.remote.server
                .withUsernameAndPassword("my-username", "my-password")
            )
        )
    )
}
```

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

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  val buildCacheCredentials = for {
    username <- sys.env.get("DEVELOCITY_CACHE_USERNAME")
    password <- sys.env.get("DEVELOCITY_CACHE_PASSWORD")
  } yield UsernameAndPassword(username, password)

  previous
    .withBuildCache(
      previous.buildCache
        .withRemote(
          previous.buildCache.remote
            .withServer(
              previous.buildCache.remote.server
                .withUsernameAndPassword(buildCacheCredentials)
            )
        )
    )
}
```

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

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withRemote(
          previous.buildCache.remote
            .withEnabled(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`.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withRemote(
          previous.buildCache.remote
            .withStoreEnabled(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 (for example, 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.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withRemote(
          previous.buildCache.remote
            .withServer(
              previous.buildCache.remote.server
                .withUseExpectContinue(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`.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withRemote(
          previous.buildCache.remote
            .withServer(
              previous.buildCache.remote.server
                .withAllowInsecureProtocol(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="disabling-local-and-remote-caching-for-a-subproject"></a>

#### Disabling Local and Remote Caching for a Subproject

Setting the `develocityBuildCacheClient` task to `None` will disable local and remote caching for a specific subproject. Doing so disables caching for all supported tasks on the specific subproject.

**build.sbt:**

```
lazy val util = (project in file("util"))
  .settings(
    develocityBuildCacheClient := None
  )
```

> [!NOTE]
> It’s not possible to disable caching only for a specific task (such as compile or test).

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

### Declaring Inputs

The Build Cache works based on inputs and outputs. For each [supported tasks](#cacheable_tasks) 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 task is executed, and the outputs are stored in the Build Cache under the cache key.

This section explains how inputs can be fine-tuned.

<a id="adding_additional_inputs"></a>

#### Adding Additional Inputs

The predefined inputs of any supported task can be augmented via the `develocityTaskCacheKeyComponents` setting (refer to [caching the compile task](#caching_the_compile_task) and [caching the test tasks](#caching_the_test_testonly_and_testquick_tasks) for the predefined inputs captured by the supported tasks).

A common use case for declaring additional inputs is to track files that aren’t in the classpath, as any edit to such files may change the result of running the tests. Without additional configuration, the Build Cache is unaware of these additional inputs and will therefore load the result from the Build Cache even if these files have been edited. That would lead to incorrectness.

The following example adds the directory `test-files` as an input to all executions of the `test`, `testOnly`, and `testQuick` tasks. Files in this folder will be used to compute the cache key:

**build.sbt:**

```
import com.gradle.develocity.agent.sbt.api.experimental.buildcache
import sbt.nio.FileStamper
import java.nio.file.Path
lazy val extraTestFiles = taskKey[Seq[Path]]("Extra files that affect test execution")
extraTestFiles := {
  val directory = (ThisBuild / baseDirectory).value / "test-files"
  directory.allPaths.get.map(_.toPath)
}
// Tell sbt to compute hashes instead of using a last modified time for the extra test files for better stability
// See https://www.scala-sbt.org/1.x/docs/Howto-Track-File-Inputs-and-Outputs.html#File+change+tracking
extraTestFiles / outputFileStamper := FileStamper.Hash
// `extraTestFiles / outputFileStamps` retrieves the hashes of the extra test files that are captured as inputs
Test / test / buildcache.develocityTaskCacheKeyComponents += (extraTestFiles / outputFileStamps).taskValue
Test / testOnly / buildcache.develocityInputTaskCacheKeyComponents += (extraTestFiles / outputFileStamps).taskValue
Test / testQuick / buildcache.develocityInputTaskCacheKeyComponents += (extraTestFiles / outputFileStamps).taskValue
```

It’s worth noting that `buildcache.develocityTaskCacheKeyComponents` expects that for each passed task of type `T` there is an implicit instance of `Hashable[T]` available.

In the above example, `(extraTestFiles / outputFileStamps)` is of type `Seq[(Path, FileStamp)]`, and there is an implicit `Hashable[Seq[(Path, FileStamp)]]` available. Inspect the [`Hashable`](https://docs.gradle.com/downloads/sbt-plugin-javadoc/1.4.5/com/gradle/develocity/agent/sbt/api/experimental/hash/Hashable$.html) module to know what other `Hashable` implicit instances are available.

<a id="defining_an_hashable_implicit_instance"></a>

##### Defining an Hashable Implicit Instance

There are situations when an implicit instance of `Hashable` needs to be defined. For instance, imagine adding a task of type `ConciseProjectInfo` to the inputs tracked by the `compile` task:

**build.sbt:**

```
import com.gradle.develocity.agent.sbt.api.experimental.buildcache
lazy val conciseProjectInfo = taskKey[ConciseProjectInfo](https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/"Provides concise information about the project")
conciseProjectInfo := ConciseProjectInfo(name.value, startYear.value)
Compile / compile / buildcache.develocityTaskCacheKeyComponents += conciseProjectInfo.taskValue
```

This build file will fail to compile because an implicit instance of `Hashable[ConciseProjectInfo]` isn’t available. [`Hashable`](https://docs.gradle.com/downloads/sbt-plugin-javadoc/1.4.5/com/gradle/develocity/agent/sbt/api/experimental/hash/Hashable$.html) is a functional interface defined as:

**Hashable.scala:**

```
package com.gradle.develocity.agent.sbt.api.experimental.hash
trait Hashable[T] {
  def digest(t: T, hasher: Hasher): Unit
}
```

Where [`Hasher`](https://docs.gradle.com/downloads/sbt-plugin-javadoc/1.4.5/com/gradle/develocity/agent/sbt/api/experimental/hash/Hasher.html) is an object that can be fed with primitive types, strings, and can compute a hash.

Next is how an implicit instance of `Hashable[ConciseProjectInfo]` can be defined:

**build.sbt:**

```
import com.gradle.develocity.agent.sbt.api.experimental.buildcache
import com.gradle.develocity.agent.sbt.api.experimental.hash.Hashable
import java.nio.charset.StandardCharsets
implicit val hashable: Hashable[ConciseProjectInfo] = (info, hasher) => {
  hasher.putString(info.name, StandardCharsets.UTF_8)
  Hashable.hash(info.startYear, hasher)
}

lazy val conciseProjectInfo = taskKey[ConciseProjectInfo](https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/"Provides concise information about the project")
conciseProjectInfo := ConciseProjectInfo(name.value, startYear.value)
Compile / compile / buildcache.develocityTaskCacheKeyComponents += conciseProjectInfo.taskValue
```

<a id="solving_problems_with_build_caching"></a>

### Troubleshooting

While working with the Build Cache you may encounter situations where build results are not 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

Logging of the Build Cache related operations in the sbt plugin can be increased to make analyzing problems with Build Caching easier. To enable verbose logging of the Build Cache, set the system property `develocity.internal.cache.verbose`. By default, these additional log messages are logged at the `DEBUG` level. To set a different level, such as `INFO` for example, set the system property `develocity.internal.cache.defaultLogLevel` to the desired log level.

```shell
sbt -Ddevelocity.internal.cache.verbose=true -Ddevelocity.internal.cache.defaultLogLevel=info "proj / test"
```

**Output:**

```
(...)
[info] Computing cache key 'proj / Test / test / develocityTaskCacheKey' (8 components):
[info] Hashing 'proj / Test / develocityFilteredTests' produced '585e21fb889d22eb339ba04f7b41bd66'
[info] Hashing '. / loadedTestFrameworks' produced '4947226c97108a368683000852bf4f2f'
[info] Hashing 'proj / Test / test / testExecution' produced '6c4c2b176720e67d3aa1c5a74af016bd'
[info] Hashing 'proj / Test / develocityFullClasspath / outputFileStamps' produced '8e20e5baf456d5605a42e5d5fc404806'
[info] Hashing '. / testForkedParallel' produced 'b55cff6ee5ab10468335f878aa2d6251'
[info] Hashing 'Global / javaOptions' produced 'bc764cd8ddf7a0cff126f51c16239658'
[info] Hashing '. / classLoaderLayeringStrategy' produced '1f6ccf510fa0af8381f1791b6198e06d'
[info] Hashing '. / name' produced '641a7d11054d49795fac52da80768b9e'
[info] Computed cache key 'proj / Test / test / develocityTaskCacheKey': 'b3bdb1976a81fb040f5f2d94fd9a25b8'
(...)
```

<a id="finding_the_cause_of_cache_misses"></a>

#### Finding the Cause of Cache Misses

Sometimes you might encounter a situation where a task 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 tasks](#cacheable_tasks) 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, for example, a timestamp being added to a file by some build logic. To identify which inputs change between builds, compare the output of sbt with verbose Build Cache logging enabled, as shown above.

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

## Viewing Test Results

The Develocity plugin 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 compatible with sbt’s test interface 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’re using.

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

### 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 _task_, _test suite_ and finally _test case_. Next to each _task_, _test suite_ and _test case_, the outcome and the total time are displayed.

![Test Overview Page Example](https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/../_images/test-overview.png)

Test Overview Page Example

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

#### Test Outcomes

Every _task_, _test suite_ and _test case_ has an outcome. For a _task_, the outcome is either `SUCCESS`, indicating that the _task_ 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 task, the _total time_ corresponds to the total amount of wall-clock time the task 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. The _total time_ is unavailable when tests run in a forked JVM.

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

![Test Suite Details Page Example](https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/../_images/test-suite-details.png)

Test Suite Details Page Example

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. A _test case_'s duration may be missing in case the test framework didn’t report it.

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

![Test Case Details Page Example](https://docs.gradle.com/develocity/sbt/1.4/sbt-plugin/../_images/test-case-details.png)

Test Case Details Page Example

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="known-limitations"></a>

### Known Limitations

<a id="missing-durations-when-forking"></a>

#### Missing Durations When Forking

Because of how sbt reports test events in case a test suite runs in a forked JVM, it’s not possible to accurately report a _test suite_'s _total_, _own_ and _serial_ durations. Because these durations are missing, the parent _test task_'s _own_ and _serial_ durations aren’t available either when tests are run in a forked JVM.

<a id="missing-test-case-durations"></a>

#### Missing Test Case Durations

Some test frameworks don’t report the duration of a test case duration, and Develocity can therefore not show that information on the test results pages. The following test frameworks are known not to report test case durations:

*   [ScalaCheck](https://scalacheck.org)
    
*   [MUnit](https://scalameta.org/munit/) when running on Scala JS
    

When a _test case_ duration isn’t reported, the following durations will also not be available in Develocity (because they can’t be computed correctly):

*   The _test case total duration_
    
*   Its parent _test suite own and serial duration_
    
*   Its parent _task serial time_
    

<a id="using_test_retry"></a>

## Using Test Retry

When test retry is enabled, any failed tests are retried after all the tests have been executed. The process repeats with tests that continue to fail until the maximum specified number of retries has been attempted, or there are no more failing tests.

By default, the test task doesn’t fail when all failed tests pass after a retry. This setting can be changed so that tests that pass on retry cause the task to fail.

Tests that initially fail but pass on retry are considered “flaky” by Develocity. For 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/).

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

### Configuration

Test retry isn’t enabled by default. It can be enabled and configured via the `testRetry` field of the `develocityConfiguration` object. The initialization below shows the default configuration.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withTestRetry(
      previous.testRetry
        .withFlakyTestPolicy(FlakyTestPolicy.DoNotFail) (1)
        .withMaxRetries(0) (2)
        .withMaxFailures(0) (3)
        .withClassesFilter((name: String, annotations: List[String]) => true) (4)
        .withClassRetryFilter((name: String, annotations: List[String]) => false) (5)
    )
}
```

1. Whether the build should fail if there are failing tests, even if they eventually pass. Use FlakyTestPolicy.Fail to fail the build if flaky tests are detected.
2. The maximum number of times to retry an individual test. Test retry is disabled unless this value is greater than zero.
3. The maximum number of test failures allowed before retrying is disabled for the current test run. This setting defaults to 0, which results in no limit.
4. A filtering function to determine which test classes are eligible for retry. By default, all classes are eligible.
5. A filtering function to determine which test classes should be entirely retried, instead of only their failing test cases. By default, no classes are entirely retried.

<a id="retrying-only-some-tests"></a>

### Retrying Only Some Tests

By default, all tests are eligible for retrying. The field `classesFilter` of the test retry configuration can be used to control which tests should be retried and which shouldn’t.

The decision to retry a test or not is based on the tests reported class name, regardless of the name of the test case or method. The annotations present or not on this class can also be used as the criteria.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withTestRetry(
      previous.testRetry
        .withMaxRetries(3)
        .withClassesFilter { (name, annotations) =>
          // name is a fully qualified class name
          // annotations is the list of fully qualified annotation class names
          name == "com.example.MyKnownFlakyTest" || annotations.contains("com.example.Retryable")
        }
    )
}
```

<a id="retrying-on-class-level"></a>

### Retrying on Class-Level

By default, individual tests are retried. The `classRetryFilter` field of the test retry configuration can be used to control which test classes must be retried as a whole unit. To retry a class as a whole, the class must also satisfy the `classRetryFilter` predicate.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withTestRetry(
      previous.testRetry
        .withMaxRetries(3)
        .withClassRetryFilter { (name, annotations) =>
          // name is a fully qualified class name
          // annotations is the list of fully qualified annotation class names
          name.endsWith("IntegrationTest") || annotations.contains("com.example.RetryClass")
        }
    )
}
```

<a id="retrying-only-for-ci-builds"></a>

### Retrying Only for CI Builds

You may find that local developer builds don’t benefit much from retry behavior, particularly when those tests are invoked via your IDE. In that case, we recommend enabling retry only for CI builds.

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withTestRetry(
      previous.testRetry
        .withMaxRetries(if (sys.env.contains("CI")) 3 else 0) (1)
        .withMaxFailures(20)
        .withFlakyTestPolicy(FlakyTestPolicy.DoNotFail)
    )
}
```

1. Enable retrying tests only when running in CI

<a id="compatibility-matrix"></a>

### Compatibility Matrix

The test retry functionality should work with all test frameworks and all sbt versions supported by Develocity. However, some test frameworks support only suite-wide retries: if a test case fails in a test suite, the entire test suite is retried.

Please refer to the compatibility matrix below to learn about possible limitations.

<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 16.6666%;"> <col style="width: 33.3333%;"> <col style="width: 50.0001%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top">Test framework</td><td class="tableblock halign-left valign-top">Retry entire test class</td><td class="tableblock halign-left valign-top">Retry only failed test cases</td></tr><tr><td class="tableblock halign-left valign-top">ScalaTest</td><td class="tableblock halign-left valign-top">Supported in all versions</td><td class="tableblock halign-left valign-top">Supported in all versions</td></tr><tr><td class="tableblock halign-left valign-top">JUnit</td><td class="tableblock halign-left valign-top">Supported in all versions</td><td class="tableblock halign-left valign-top">Requires junit-interface v0.13.3 or later</td></tr><tr><td class="tableblock halign-left valign-top">MUnit</td><td class="tableblock halign-left valign-top">Supported in all versions</td><td class="tableblock halign-left valign-top">Requires MUnit v1.0.0-M3 or later</td></tr><tr><td class="tableblock halign-left valign-top">ScalaCheck</td><td class="tableblock halign-left valign-top">Supported in all versions</td><td class="tableblock halign-left valign-top">Requires this patch</td></tr><tr><td class="tableblock halign-left valign-top">Specs2</td><td class="tableblock halign-left valign-top">Supported in all versions</td><td class="tableblock halign-left valign-top">Requires Specs2 v4.20.5 or later or v5.5.1 or later</td></tr><tr><td class="tableblock halign-left valign-top">ZIO Test</td><td class="tableblock halign-left valign-top">Supported in all versions</td><td class="tableblock halign-left valign-top">Requires ZIO v2.1.0 or later</td></tr><tr><td class="tableblock halign-left valign-top">µTest</td><td class="tableblock halign-left valign-top">Supported in all versions</td><td class="tableblock halign-left valign-top">Requires this patch</td></tr></tbody></table>

Contact us at [support.gradle.com](https://support.gradle.com) if the testing framework you are using is missing from the above list (or it’s not working).

<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 behavior, 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 `~/.sbt/1.0/.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 `~/.sbt/1.0/.develocity/build-scan-data/<<plugin-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
```

* * *

If you have any questions or need any assistance contact the Develocity support team or your customer success representative.

<a id="api-reference"></a>

## Appendix A: API reference

See the [API reference](https://docs.gradle.com/downloads/sbt-plugin-javadoc/1.4.5/index.html) for more details about programmatic configuration of the Develocity sbt plugin.

The [Common Custom User Data sbt Plugin](https://github.com/gradle/common-custom-user-data-sbt-plugin) provided by Gradle Inc. provides an example. This plugin can be applied directly to your project, or can serve as a template project for your own plugin implementation.

<a id="settings-and-tasks-provided-by-the-develocity-sbt-plugin"></a>

## Appendix B: Settings and tasks provided by the Develocity sbt plugin

The following settings and tasks are exposed by the Develocity sbt plugin.

<a id="settings"></a>

### Settings

<a id="develocityconfiguration"></a>

#### develocityConfiguration

Stores the [DevelocityConfiguration](https://docs.gradle.com/downloads/sbt-plugin-javadoc/1.4.5/com/gradle/develocity/agent/sbt/api/configuration/DevelocityConfiguration.html).

<a id="tasks"></a>

### Tasks

<a id="develocitybuildscanpublishprevious"></a>

#### develocityBuildScanPublishPrevious

Publishes the Build Scan captured by the last build.

<a id="develocityprovisionaccesskey"></a>

#### develocityProvisionAccessKey

Authenticates your build environment with Develocity.

<a id="develocityenableremotecacheaftererror"></a>

#### develocityEnableRemoteCacheAfterError

Re-enabled the remote Build Cache after it was disabled due to errors.

This task is only available in Develocity sbt plugin 1.4.1 or later.

<a id="captured_information"></a>

## Appendix C: Captured information

The Develocity sbt plugin 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 tasks, 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 aren’t 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 sbt plugin and transmitted in a Build Scan.

*   Environment
    
    *   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 commands, switches)
        
    *   Build console output
        
    *   Build failure exception messages and stack traces
        
    *   <a id="toggler-task-content"></a>Executed tasks ▶
        
        <a id="task-content"></a>
        
        *   Task name
            
        *   Task scope
            
        
    *   <a id="toggler-build-tests-content"></a>Executed tests ▶
        
        <a id="build-tests-content"></a>
        
        *   Test names (e.g. class and method name), outcome and duration
            
        *   Console output
            
        *   Failure exception messages and stack traces
            
        
    *   Background Build Scan publication
        
    

<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="additional_build_cache_details"></a>

## Appendix D: Additional Build Cache Details

<a id="cacheable_tasks"></a>

### Cacheable Tasks

The plugin caches the following tasks out of the box:

*   `Compile / compileIncremental`
    
*   `Test / compileIncremental`
    
*   `Test / executeTests`
    
*   `Test / testOnly`
    
*   `Test / testQuick`
    
*   `IntegrationTest / compileIncremental`
    
*   `IntegrationTest / executeTests`
    
*   `IntegrationTest / testOnly`
    
*   `IntegrationTest / testQuick`
    

<a id="enabling_build_cache_in_a_custom_sbt_configuration"></a>

### Enabling Build Cache in a Custom `sbt.Configuration`

By default, Develocity configures Build Caching for compilation and test results in the `Compile`, `Test` and `IntegrationTest` configurations.

If your build defines or uses additional `sbt.Configuration`, then you will need to add the Develocity Build Cache settings in that configuration:

**build.sbt:**

```
val Custom = config("Custom")
val myProject = project.in(file("myProject"))
  .settings(
    // Your usual settings
    // Add this for each Configuration you want to enable Build Cache for:
    DevelocityPlugin.develocitySettings(Custom)
  )
```

If your custom configuration extends the `Test` configuration, then Develocity will automatically inject the settings required for flaky test detection, test retry, etc., along with the settings required to enable Build Caching.

If your custom configuration doesn’t extend the `Test` configuration, but you still want to inject the settings required for features related to testing, then you can pass the optional argument `forceTestSettings = true` to the `develocitySettings` method:

**build.sbt:**

```
DevelocityPlugin.develocitySettings(Custom, forceTestSettings = true)
```

<a id="understanding-requireclean-true"></a>

### Understanding `requireClean = true`

By default, the Build Cache is configured with `requireClean = true` to allow only clean builds to push to the Build Cache. This behavior can be switched on or off by setting the `requireClean` property in `BuildCache`:

**builds.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildCache(
      previous.buildCache
        .withRequireClean(false)
    )
}
```

<a id="what_is_a_clean_build"></a>

#### What Is a Clean Build?

In sbt, cleaning is done by executing the `clean` task. Like any other task, it’s executed in a given scope. After the clean task has run in a scope `S`, Develocity will consider that this scope `S` is clean, and will allow tasks running in this scope to push to the Build Cache.

In batch mode, if a scope has been cleaned, it remains clean for the entire sbt execution. In interactive mode, a clean scope will remain clean until the next task engine run which executes no `clean` task finishes.

<a id="verifying-a-build-is-clean"></a>

#### Verifying a Build Is Clean

To verify that a project will push results to the cache after running `clean`, you can check its clean status. For example, to verify that the compilation of `some-project` is clean after running `clean`:

```scala
sbt> clean
sbt> show some-project / Compile / develocityIsClean
[info] true
```

If the output is `true`, the build is clean and results will be pushed to the Build Cache. If the output is `false`, the build isn’t clean and results won’t be pushed to the Build Cache.

<a id="ensuring-projects-are-cleaned"></a>

#### Ensuring Projects Are Cleaned

If `develocityIsClean` returns `false` for a project, you have several options to ensure the project is cleaned when you run the `clean` task:

*   Configure your root project to aggregate the project, so that `clean` is run on the project when you run `clean` at the root level.
    
*   Define an alias for `clean` so that it runs `clean` on the project and any other projects you want to include:
    
    **build.sbt:**
    
    ```
    addCommandAlias("cleanAll", ";clean;some-project/clean;another-project/clean")
    ```
    

<a id="caching_the_compile_task"></a>

### Caching the `compile` Task

When executing `compile` in a configuration where the Build Cache has been enabled, Develocity will store the result of compilation in a cache, so that they can be reused by subsequent builds.

The cache key for the `compile` task is computed based on the following inputs:

*   The Scala compiler version
    
*   The project name
    
*   The input sources
    
*   The external dependency classpath
    
*   The incremental compiler options
    

The cache key for the `compile` task of a given project and configuration can be seen with:

```scala
sbt> show proj / Compile / compile / develocityTaskCacheKey
[info] 3f2c516d91677bbe810b373da00357b0
```

<a id="caching_the_test_testonly_and_testquick_tasks"></a>

### Caching the `test`, `testOnly` and `testQuick` Tasks

When executing `test`, `testOnly` or `testQuick` in a configuration where the Build Cache has been enabled, Develocity will store the result of the test execution in a cache, so that they can be reused by subsequent builds.

The cache key for these tasks is computed based on the following inputs:

*   The project name
    
*   The filtered tests classes
    
*   The test frameworks
    
*   The test execution configuration
    
*   The classpath
    
*   If applicable, the parsed user input (for `testOnly` and `testQuick`)
    

The cache key for the `test` task of a given project and configuration can be seen with:

```scala
sbt> show proj / Test / test / develocityTaskCacheKey
[info] 0d02cc0759ec8fe81cb436e6f7e526b8
```

The cache key for the `testOnly` and `testQuick` tasks of a given project and configuration can be seen with:

```scala
sbt> show proj / Test / testOnly / develocityInputTaskCacheKey com.example.MyTest
[info] 524f957a300cd3334b7ae633b9a5dd3b
```

<a id="compatibility-with-sbt-and-develocity"></a>

## Appendix E: Compatibility with sbt and Develocity

Compatibility between versions of sbt, Develocity, and the Develocity sbt plugin can be found in the [Develocity compatibility document](https://docs.gradle.com/develocity/2026.1/miscellaneous/compatibility/).

<a id="verifying-the-signature-of-the-plugin-jar"></a>

## Appendix F: Verifying the signature of the plugin jar

The plugin 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/sbt-develocity_2.12_1.0/1.4.5/sbt-develocity_2.12_1.0-1.4.5.jar && \
  curl -OL https://repo1.maven.org/maven2/com/gradle/sbt-develocity_2.12_1.0/1.4.5/sbt-develocity_2.12_1.0-1.4.5.jar.asc && \
  gpg --keyserver keys.openpgp.org --recv-key  7B79ADD11F8A779FE90FD3D0893A028475557671 && \
  gpg --verify sbt-develocity_2.12_1.0-1.4.5.jar.asc sbt-develocity_2.12_1.0-1.4.5.jar
```

The output of the last command should look similar to the following:

**Output:**

```
gpg: Signature made Thu Oct 12 12:27:12 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 sbt plugin is revoked. Verifying the signature of these prior versions is no longer possible.

<a id="known-issues"></a>

## Appendix G: Known issues

<a id="installation"></a>

### Installation

<a id="plugin-resolution-fails"></a>

#### Plugin resolution fails

sbt-develocity cannot be resolved with sbt versions older than 1.9.0.

Recommended solutions:

*   Upgrade to sbt 1.9+ if you can.
    
*   Please contact your customer success representative if you can’t.
    

<a id="build-scan"></a>

### Build Scan

<a id="build-cancellation"></a>

#### Build cancellation

On Linux and macOS, no Build Scan is published when a build is canceled. This is intentional, as incomplete Build Scans could cause errors or misleading information within Develocity. On Windows, however, a Build Scan is published even after cancellation. Be aware that these scans might contain errors or unexpected data when viewed in Develocity; it’s recommended that you remove them manually.

<a id="console-log"></a>

### Console Log

<a id="log-messages-via-slog"></a>

#### Log Messages via `sLog`

The Develocity sbt plugin doesn’t capture logs generated via the sbt `sLog` logger (refer to the sbt documentation [log messages in a setting](https://www.scala-sbt.org/1.x/docs/Howto-Logging.html#Log+messages+in+a+setting) for details).

If `sLog` is used inside a task definition, use `streams.value.log.info` instead (as suggested by the [related](https://www.scala-sbt.org/1.x/docs/Howto-Logging.html#Log+messages+in+a+task) sbt documentation) for the log messages to be captured in Build Scans.

If `sLog` is used inside a setting definition, no workaround is available.

<a id="failure"></a>

### Failure

<a id="javax-management-instancealreadyexistsexception-when-log4j-is-used"></a>

#### javax.management.InstanceAlreadyExistsException When Log4J Is Used

If log4j usage for internal loggers is enabled via the setting `ThisBuild / useLog4J := true`, an unexpected `javax.management.InstanceAlreadyExistsException` may be thrown. This is a [known issue](https://issues.apache.org/jira/browse/LOG4J2-3121) that can be ignored.

<a id="failures-happening-outside-the-task-engine-arent-reported"></a>

#### Failures Happening Outside the Task Engine Aren’t Reported

_(sbt version <1.10.0)_

If a failure occurs outside of task evaluation (i.e. purely in an sbt _command_), then the failure won’t be registered by Develocity, and the build won’t be marked as failed in Develocity.

**build.sbt:**

```
commands += Command.command("fail") { _ => ??? } // No failure reported when calling `fail` in sbt < 1.10.0
```

The workaround is to update to sbt `1.10.0` or later.

<a id="deprecations"></a>

## Appendix H: Deprecations

<a id="system-properties"></a>

### System Properties

_(sbt plugin 1.1+)_

In the Develocity sbt plugin 1.1 the following properties were renamed:

*   `gradle.scan.uploadInBackground` was renamed to `develocity.scan.uploadInBackground`
    
*   `gradle.scan.captureBuildLogging` was renamed to `develocity.scan.captureBuildLogging`
    

If the build is using the old property names, a warning message will be displayed in the build log. To avoid the warning message, update the build to use the new property names.

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

### Configuration

_(sbt plugin 1.1.2+)_

In version 1.1.2 of the Develocity sbt plugin, the `apply` methods used to create various configuration objects were deprecated. Instead of using the `apply` methods, create new instances from previous values using the respective `withXXX` methods. Each call to `withXXX` will create a copy of the configuration object and set the field `XXX` to the provided value. All other values are taken from the configuration object being accessed.

For example, to disable the background uploading of the Build Scan, the following code should be used to change the `backgroundUpload` value of the default `BuildScan` configuration:

**build.sbt:**

```
ThisBuild / develocityConfiguration ~= { previous =>
  previous
    .withBuildScan(
      previous.buildScan
        .withBackgroundUpload(false)
    )
}
```

See the [DevelocityConfiguration](https://docs.gradle.com/downloads/sbt-plugin-javadoc/1.4.5/com/gradle/develocity/agent/sbt/api/configuration/DevelocityConfiguration.html) API reference for more details.

If the build is using `apply` methods to create configuration objects, a warning message will be displayed in the build log. To avoid the warning message, update the build to modify the default configuration.

<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/sbt/) or any of our [sponsored open source instances](https://gradle.com/oss-sponsored-by-develocity/).