This plugin provides the capability to publish code coverage to Bitbucket. It communicates with the endpoints provided by the Bitbucket plugin: Code Coverage for Bitbucket Server.
Currently, the plugin only supports the conversion of JaCoCo reports. But thanks to its modular design, it's easily possible to extend the plugin to other report types.
The plugin is available in the Gradle Plugin Portal.
Using the plugins DSL:
plugins {
id("dev.turingcomplete.bitbucket-code-coverage") version "1.0.1"
}
Using legacy plugin application:
buildscript {
repositories {
maven {
url = uri("https://plugins.gradle.org/m2/")
}
}
dependencies {
classpath("dev.turingcomplete.bitbucket-code-coverage:1.0.1")
}
}
apply(plugin = "dev.turingcomplete.bitbucket-code-coverage")
️️
The following code must be present at least in the build.gradle.kts
for the plugin to publish JaCoCo code coverage reports:
plugins {
jacoco
id("dev.turingcomplete.bitbucket-code-coverage") version "1.0.1"
}
bitbucketCodeCoverage {
bitbucketHost.set("https://mybitbucket.com")
bitbucketCommitId.set("0835f844bc4950d7...")
}
tasks.jacocoTestReport {
reports.xml.required.set(true)
}
- Line 2: Apply the
jacoco
plugin to the project. - Line 6: Set the host address to the Bitbucket instance.
- Line 7: Set a Git commit ID, to which Bitbucket will associate the published code coverage.
- Line 10-12: Configure the
jacocoTestReport
task to produce XML report files. The plugin will convert these into the Bitbucket code coverage format.
The PublishJacocoCodeCoverageToBitbucketTask
class provides the functionality to convert JaCoCo XML report files into the Bitbucket code coverage format and can then publish them to Bitbucket.
If the project has the jacoco
plugin applied, this plugin will add
a default instance of the PublishJacocoCodeCoverageToBitbucketTask
with the name publishJacocoCodeCoverageToBitbucket
under the group verification
.
This default task depends on all JacocoReport
tasks (e.g., jacocoTestReport
) and uses their outputs as inputs and will only run if at least one of these tasks is not up-to-date. In addition, the sourceFilesSearchDirs
property will be mapped to all source directories of all SourceSets
in the projects.
The PublishJacocoCodeCoverageToBitbucketTask
has the following properties:
Property | Required | Description |
---|---|---|
bitbucketHost |
Yes | The host address to the Bitbucket instance. Must start with http:// or https:// . |
bitbucketCommitId |
Yes | The Git commit ID to which Bitbucket will associate the published code coverage. |
bitbucketProjectKey |
No | The key of a Bitbucket project. |
bitbucketRepositorySlug |
No | The slug of a repository in a Bitbucket project. |
bitbucketUser |
No | The name of a Bitbucket user. |
bitbucketPassword |
No | The password of the bitbucketUser . |
bitbucketToken |
No | An HTTP access token to use for authentication as an alternative to the user/password authentication. (The task will ignore the value if a bitbucketUser is set.) |
bitbucketTimeout |
No | The timeout of a request to Bitbucket. The default value is 30 seconds. |
jacocoXmlCoverageReports |
No | A FileCollection of JaCoCo XML report files. |
skipOnMissingJacocoXmlCoverageReports |
No | If set to true and jacocoXmlCoverageReports is empty, the task will not fail. |
sourceFilesSearchDirs |
No | Bitbucket requires file paths relative to the root project directory, but the JaCoCo reports only contain the fully qualified class name. Therefore, the task needs to search in the given directories for the source files of all classes. |
If we only set the Git commit ID via the property bitbucketCommitId
, the code coverage would be associated with all commits in all repositories that have the given ID. The property bitbucketProjectKey
and bitbucketRepositorySlug
can be specified to limit the scope of the code coverage to a commit in one repository.
The default task publishJacocoCodeCoverageToBitbucket
can be configured via the extension bitbucketCodeCoverage
. This extension provides the same properties as the task (the JaCoCo specific ones are nested under the sub extension jacoco
).
The following code shows an example of a wholly configured extension:
import java.time.Duration
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalUnit
bitbucketCodeCoverage {
bitbucketHost.set("https://mybitbucket.com") // Required
bitbucketCommitId.set("0835f844bc4950d7...") // Required
bitbucketProjectKey.set("PROJECT_KEY") // Optional
bitbucketRepositorySlug.set("repositorySlug") // Optional
bitbucketUser.set("User") // Optional
bitbucketPassword.set("Password$") // Optional
bitbucketToken.set("token1234") // Optional; alternative to the user/password
bitbucketTimeout.set(Duration.of(1, ChronoUnit.MINUTES)) // Optional
jacoco {
jacocoXmlCoverageReports.setFrom(project.files(File("report.xml"))) // Optional
skipOnMissingJacocoXmlCoverageReports.set(true) // Optional
sourceFilesSearchDirs.setFrom(project.files(File("alternate", "search-dir"))) // Optional
}
onlyRunIfJacocoReportTaskDidWork.set(false) // Optional
}
It's easy to automatically obtain the current Git commit ID and set it to the task property. One way would be to use the JGit library:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.eclipse.jgit:org.eclipse.jgit:6.1.0.202203080745-r")
}
}
bitbucketCodeCoverage {
bitbucketCommitId.set(provider { Git.open(rootProject.projectDir).repository.resolve(Constants.HEAD).name })
// ...
}
Alternatively, without a dependency, it would be possible to execute the command git rev-parse HEAD
via a ProcessBuilder
and get the Git commit ID from the output of this process.
We need to create a new task to convert other code coverage reports besides the JaCoCo ones that extend PublishCodeCoverageToBitbucketTask
. This new task must populate the property fileCodeCoverages
with instances of FileCodeCoverage
. These contain the code coverage information of a single source code file.
An example task could look as follows:
class CustomPublishCodeCoverageToBitbucketTask : PublishCodeCoverageToBitbucketTask() {
init {
val myClassCodeCoverage = FileCodeCoverage(sourceFile = File("src/main/java/MyClass.java"),
fullyCoveredLines = setOf(1, 2),
partiallyCoveredLines = setOf(3, 4),
uncoveredLines = setOf(5, 6))
fileCodeCoverages.set(listOf(myClassCodeCoverage))
}
}
Copyright (c) 2022 Marcel Kliemannel
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the LICENSE for the specific language governing permissions and limitations under the License.