The Develocity sbt plugin improves your development workflow and your productivity, when developing and maintaining sbt builds. The plugin enables Build Scan insights and supports publishing to scans.gradle.com.

To publish sbt build scans, Develocity version 2023.2+ is required. This manual is optimized for Develocity version 2024.1+ with Develocity sbt plugin version 1.0+.

sbt Build Scan functionality is not enabled by default with Develocity. If you wish to use sbt with Develocity, please contact your customer success representative.

Getting set up

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

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

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 once you load your build.

project/plugins.sbt
addSbtPlugin("com.gradle" % "sbt-develocity" % "1.0.1")

Connecting to Develocity

All Develocity settings are grouped under one configuration object that is recommended to be set in the ThisBuild scope. Unless you intend to publish build scans to scans.gradle.com, the minimum configuration required is that of the Develocity server URL.

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    server = Server(
      url = Some(url("https://develocity.mycompany.com"))))

Other configuration options will be described as we introduce each feature, but you can find all configuration options in the configuration reference.

Allowing untrusted SSL communication

If your Develocity server uses an SSL certificate that is not trusted by your build’s Java runtime, you will not 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 :=
  DevelocityConfiguration(
    server = Server(
      url = Some(url("https://develocity.mycompany.com")),
      allowUntrusted = 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.

Authenticating

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. If your instance doesn’t require authentication, you can skip to Using build scans

Develocity access keys should be treated with the same secrecy as passwords. They are used to authorize access to Develocity from a build.

Automated access key provisioning

The easiest way to configure a build environment to authenticate with Develocity is to use the develocityProvisionAccessKey task.

$ 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 are not 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 will not 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.

If your browser cannot be opened automatically at the correct page, you will be asked to manually open a link provided in the build console.

Manual access key configuration

Access keys can be configured manually for an environment, when automated provisioning is not suitable.

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.

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

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.

develocity.mycompany.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq

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

Via environment variable

The access key may also be specified via the 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».

$ export DEVELOCITY_ACCESS_KEY=develocity.mycompany.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.

$ export DEVELOCITY_ACCESS_KEY=develocity1.mycompany.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq;develocity2.mycompany.com=9y4agfiubqqjea4vonghohvuyra5bnvszop4asbqee3m3sm67w5k && \
  sbt compile
Via configuration

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

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    server = Server(
      url = Some(url("https://develocity.mycompany.com")),
      accessKey = Some("7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq")))
Short-lived access tokens

Develocity access keys are long-lived, creating risks if they are 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.

Develocity server version 2024.1+ supports access tokens.
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 wish 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 of each permission you wish 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 wish 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-seperated lists or repeated parameters. For example, ?projectIds=a,b&projectIds=c is valid and will request projects a, b, and c.

If project-level access control is not enabled, all access tokens will be granted the “Access all data without an associated project” permission even if it is not explicitly requested.

If the user creating the token does not 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.

See the API documentation for more details on the /api/auth/token endpoint.

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

$ curl -X POST https://ge.mycompany.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.

Connecting to scans.gradle.com

A free version of Develocity is available at scans.gradle.com, 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/help/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 :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      termsOfUse = Some(url("https://gradle.com/help/legal-terms-of-use") -> true)))

Once you have accepted the terms of use, you can start publishing build scans to scans.gradle.com.

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

Using build scans

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

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.

Interactive mode

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

sbt:test-project> compile
[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...
[info] https://develocity.mycompany.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:

sbt:test-project> compile; test
[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...
[info] https://develocity.mycompany.com/s/lmzvnpyy66vbw
[info] compiling 2 Scala sources to test-project/core/target/scala-2.13/test-classes ...
...
[info] Publishing build scan...
[info] https://develocity.mycompany.com/s/pg37gbjxr4sxo

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.

$ sbt compile test
[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...
[info] https://develocity.mycompany.com/s/5563ccpas77ua

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.

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 :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      publishing = Publishing.onlyIf(_ => true)))

If you want to deactivate build scans for a particular build, you can pass the -Dscan=false system property to sbt.

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 :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      publishing = Publishing.onlyIf(_ => false)))

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

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

Publishing based on criteria

Many of you will want a bit more control over exactly when build scans are published without resorting to using -Dscan each time. Perhaps you only want to publish build scans when the build fails, or if the build is running on your continuous integration infrastructure. Such scenarios are covered by the options in the following code snippets.

Limit publishing of build scans on failure
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      publishing = Publishing.onlyIf { ctx =>
        ctx.buildResult.failures.nonEmpty
      }))
Restricting build scans to CI builds
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      publishing = Publishing.onlyIf { _ =>
        sys.env.contains("CI")
      }))

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.

Disabling programmatically

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      backgroundUpload = false))

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

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      backgroundUpload = !sys.env.get("CI").exists(_.toBoolean)))

Disabling via system property

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

$ sbt -Dgradle.scan.uploadInBackground=false compile test publish

Configuring project identifier

Detailed information regarding project-level access control can be found here.

Versions before 0.10 of this plugin do not allow specifying project identifier

Configuring programmatically

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    projectId = ProjectId("myProject"))

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.

$ sbt -Ddevelocity.projectId=myProject compile test publish

Capturing build output

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

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 a lot of console log messages that are irrelevant for your use of build scans.

How to disable

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

Programmatically

To disable programmatically, set the buildLogging option to false:

Disabling build output capturing
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      capture = Capture(
        buildLogging = false)))
Via system property

To disable without modifying the build script, supply the gradle.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.

$ sbt -Dgradle.scan.captureBuildLogging=false compile test publish

Extending build scans

You can easily include extra custom information in your build scans in the form of tags, links and values. This is a very powerful mechanism for capturing and sharing information that is important to your build and development process.

This information can be anything you like. You can tag all builds run by your continuous integration tool with a CI tag. You can capture the name of the environment that the build published to as a value. You can link to the source revision for the build in an online tool such as GitHub. The possibilities are endless.

You can see how the custom data appears in figures 1 and 2:

scan with custom data 1
Figure 1. A build scan containing tags and links
scan with custom data 2
Figure 2. 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 are filtering for all build scans that have the tag "CI" and a git branch name of "main":

build scan filtered list
Figure 3. A filtered list of build scans in Develocity

Adding tags

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

They can be added at build time via the buildScan.tags field:

Adding tags to a build’s build scans
ThisBuild / develocityConfiguration :=
    DevelocityConfiguration(
      buildScan = BuildScan(
        tags = Set(
          if (sys.env.contains("CI")) "CI" else "Local",
          sys.props("os.name"))))
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 are displayed in alphabetical order, with any all-caps labels displayed before the rest.

There are limits on captured tags:

  • maximum tag count: 50

  • maximum tag length: 200 characters

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.links field:

Adding a VCS URL to a build’s build scans
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      links = Map(
        "VCS" -> url(s"https://github.com/myorg/sample/tree/${sys.props("vcs.branch")}"))))
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, which shows how a label Source becomes a hyperlink that anyone viewing the build scan can follow.

There are limits on captured links:

  • maximum link count: 20

  • maximum link label length: 100 characters

  • maximum link url length: 100,000 characters

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.

Adding custom values to a build’s build scans
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      values = Map(
        "Scala version" -> scalaVersion.value)))
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.

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

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.

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

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

Use Coursier

Indicates whether the build was configured to use Coursier for dependencies resolution.

Coursier is the default dependency resolution library since sbt 1.3.0

Offline

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

Turbo

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

Turbo mode is available since sbt 1.3.0.

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.

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.

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.

Obfuscating identifying data

Build scans capture certain identifying information such as the operating system username, hostname and network addresses. You may choose to obfuscate this data so that it is not decipherable in build scans when viewed, 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 :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      obfuscation = Obfuscation(
        username = _.reverse)))

Obfuscating the hostnames:

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      obfuscation = Obfuscation(
        hostname = _.toCharArray.map(_.getNumericValue).mkString("-"))))

Obfuscating the IP addresses:

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    buildScan = BuildScan(
      obfuscation = Obfuscation(
        ipAddresses = _.map(_ => "0.0.0.0"))))

Capturing test data in a custom configuration

sbt supports user-defined custom configuration. By default, the Develocity sbt plugin sets up the Test and IntegrationTest configurations to capture test data.

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

build.sbt
// The configuration for which you wish 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)
)

Viewing test results

Develocity 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 is compatible with sbt’s test interface should work with Develocity without requiring any change. Contact us at support.gradle.com if the results are not correctly displayed for the test framework that you are using.

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 are grouped by task, test suite and finally test case. Next to each task, test suite and test case, the outcome and available timings are displayed.

test overview

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, or FAILED, which means that at least one test suite has failed.

For test suites, the outcome can be SKIPPED, meaning that the test suite was not executed, SUCCESS, meaning that all test cases were successful, FAILED, indicating that at least one test case was failed, or FLAKY, meaning that at least one test case was successful only after retry.

For test cases, the outcome can be SKIPPED, meaning that the test case was not 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.

Task timings

On a task, the total time corresponds to the total amount of wall clock time the task took to execute.

The own time is the time spent setting up the test execution. In other words, it is the time spent executing the task before and after the actual test execution happened. When tests are running in a forked JVM, this duration is unavailable.

The serial time is the sum of the test task own time and its test suites' own times. When tests are running in a forked JVM or any test case run by this task does not report its duration, this duration is unavailable.

Test suite timings

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.

The own time is the time spent setting up the test suite. In other words, it is the time spent executing the test suite before and after the test cases executions happened. The own time is unavailable when tests run in a forked JVM or a test case does not report its duration.

The serial time is the sum of the test task own time and its test cases durations. The serial time is unavailable when tests run in a forked JVM or any test case does not report its duration.

Test case timings

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.

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

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

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

Test case details page

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

test suite details

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.

Known limitations

Missing durations when forking

Because of how sbt reports test events in case a test suite runs in a forked JVM, it is 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 are not available either when tests are run in a forked JVM.

Missing test case durations

Some test frameworks do not 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:

When a test case duration is not 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

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 does not 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, please consult the Develocity Flaky Test Detection Guide.

Configuration

Test retry is not enabled by default. It can be enabled and configured via the testRetryConfiguration field of the develocityConfiguration object. The initialization below shows the default configuration.

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    testRetryConfiguration = TestRetryConfiguration(
      flakyTestPolicy = FlakyTestPolicy.DoNotFail, (1)
      maxRetries = 0, (2)
      maxFailures = 0, (3)
      classesFilter = (name: String, annotations: List[String]) => true, (4)
      classRetryFilter = (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.

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

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 :=
  DevelocityConfiguration(
    testRetryConfiguration = TestRetryConfiguration()
      .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")
      }
  )

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 :=
  DevelocityConfiguration(
    testRetryConfiguration = TestRetryConfiguration()
      .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")
      }
  )

Retrying only for CI builds

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

build.sbt
ThisBuild / develocityConfiguration :=
  DevelocityConfiguration(
    testRetryConfiguration = TestRetryConfiguration()
      .withMaxRetries(if (sys.env.contains("CI")) 3 else 0) (1)
      .withMaxFailures(20)
      .withFlakyTestPolicy(FlakyTestPolicy.DoNotFail)
  )
1 Enable retrying tests only when running in CI

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.

Test framework

Retry entire test class

Retry only failed test cases

ScalaTest

Supported in all versions

Supported in all versions

JUnit

Supported in all versions

Requires junit-interface v0.13.3 or later

MUnit

Supported in all versions

Requires MUnit v1.0.0-M3 or later

ScalaCheck

Supported in all versions

Requires this patch

Specs2

Supported in all versions

Requires Specs2 v4.20.5 or later or v5.5.1 or later

ZIOTest

Supported in all versions

Requires ZIO v2.0.21 or later

µTest

Supported in all versions

Requires this patch

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

Troubleshooting

Failed background build scan uploads

When using background build scan uploading (default behaviour, see this section for configuration options) upload failures are not 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 does not 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.

Slow resolution of host name

Build scans attempt to determine the host name of the machine. An issue affecting macOS can cause a delay when doing this in some environments.

If you see a warning during your build that resolving the local host name is slow, you can workaround the problem by adding a host name mapping to your /etc/hosts file.

Add these lines to your /etc/hosts file, substituting your computer name for 'mbpro' in the below snippet:

/etc/hosts
127.0.0.1   localhost mbpro.local
::1         localhost mbpro.local

Appendix A: API reference

Please see the API reference for more details about programmatic configuration of the Develocity sbt plugin.

The Common Custom User Data sbt Plugin developed 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.

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

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

Tasks

develocityBuildScanPublishPrevious

Publishes the build scan captured by the last build.

develocityProvisionAccessKey

Authenticates your build environment with Develocity.

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 are not captured. However, error messages emitted by compilers or errors in tests may reveal aspects of the source code.

Listing

The list below details the notable information captured by the Develocity sbt plugin and transmitted in a build scan.

  • Environment

    • Username (system property 'user.name') (Can be obfuscated)

    • Local hostname (environment variable 'COMPUTERNAME' / 'HOSTNAME') (Can be obfuscated)

    • Public hostname (Can be obfuscated)

    • Local IP addresses (Can be obfuscated)

    • Build Java Virtual Machine

    • Operating System

    • Hardware

  • Build

    • Build invocation options (e.g. requested commands, switches)

    • Build console output

    • Build failure exception messages and stacktraces

    • Executed tasks

    • Background build scan publication

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 scans.gradle.com are viewable by anyone with the link assigned when publishing the build scan. Links to individual build scans are not discoverable and cannot be guessed, but may be shared.

Appendix D: Release history

1.0.1

12th April 2024
  • [FIX] IntelliJ build import hangs when publishing build scans to scans.gradle.com

Compatible with scans.gradle.com and Develocity 2024.1 or later.

1.0

19th March 2024
  • [NEW] Build Scan: Test retry and flaky test detection

  • [NEW] Interactive prompt to accept the Terms Of Use

  • [NEW] Build Scan: Capture default charset of the build JVM

  • [NEW] Build Scan: Allow opt-out from console logs capturing

  • [NEW] Build Scan: Improve the capture of console logs from Coursier

  • [NEW] Build Scan: Capture executed tasks

  • [NEW] Build Scan: Improved task progress listener with sbt version 1.10.0-M1+

  • [NEW] Build Scan: Capture executed tests

  • [NEW] Build Scan: Do not publish build scans for state manipulation commands

  • [FIX] Develocity sbt plugin throws errors when executing remote builds

  • [FIX] Build Scan: Cancelling a command also cancels the Build Scan

  • [FIX] Build Scan: Builds using the resumeFromFailure and onFailure commands report incorrect build result

Compatible with scans.gradle.com and Develocity 2024.1 or later.

0.10.1

12th October 2023
  • [NEW] Added capture of sbt command success/error log events

  • [FIX] Use interactive mode with IntelliJ’s sbt shell

  • [FIX] Create default storage directory in correct sbt home

Compatible with scans.gradle.com and Develocity 2023.3 or later.

0.10

13th September 2023
  • [NEW] Add support for project level access control

  • [NEW] Add capturing of logs written to standard output and standard error

Compatible with scans.gradle.com and Develocity 2023.3 or later.

0.9

19th July 2023
  • [NEW] Initial beta release compatible with Develocity 2023.2

Compatible with scans.gradle.com and Develocity 2023.2 or later.

Appendix E: Compatibility with sbt and Develocity

Compatibility between versions of sbt, Develocity, and the Develocity sbt plugin can be found here.

Appendix F: Verifying the signature of the plugin jar

The plugin jar is published to Maven Central alongside its signature (cf. OSSRH Guide). The public key is published to https://keys.openpgp.org. You can verify the signature as follows:

$ curl -OL https://repo1.maven.org/maven2/com/gradle/sbt-develocity_2.12_1.0/1.0.1/sbt-develocity-1.0.1.jar && \
  curl -OL https://repo1.maven.org/maven2/com/gradle/sbt-develocity_2.12_1.0/1.0.1/sbt-develocity-1.0.1.jar.asc && \
  gpg --keyserver keys.openpgp.org --recv-key  7B79ADD11F8A779FE90FD3D0893A028475557671 && \
  gpg --verify sbt-develocity-1.0.1.jar.asc sbt-develocity-1.0.1.jar

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

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 (hence [unknown]). One way of establishing trust is to verify the fingerprint over a secure channel. Please contact technical support should you wish to do so.

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.

Appendix G: Known issues

Console log

Log messages via sLog

The Develocity sbt plugin does not capture logs generated via the sbt sLog logger (refer to the sbt documentation 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 sbt documentation) for the log messages to be captured in build scans.

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

Failure

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 that can be ignored.

Failures happening outside the task engine are not reported

(sbt version <1.10.0-M1)

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

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

The workaround is to update to sbt 1.10.0-M1 or later.