JaCoCo Coverage Badges for Multi-Module Projects in GitHub Actions
Vincent A. Cicirello
Posted on May 25, 2023
The jacoco-badge-generator is a GitHub Action, which can also be used locally as a command-line utility. Its functionality includes parsing JaCoCo test coverage csv files, generating coverage badges that can be displayed within the READMEs of GitHub repositories, generating a JSON summary file with the coverage percentages, and can optionally be used for pull-request coverage checks (e.g., validating if coverage is above a target minimum and/or ensuring that coverage did not decrease). It is customizable in a variety of ways (see its GitHub repository and/or the other posts in this series for details).
I just release version v2.9.0, which enhanced the existing functionality associated with using the jacoco-badge-generator GitHub Action with multi-module projects. Specifically, prior to this release, for a multi-module project, the paths to all of the JaCoCo csv reports had to be listed in the inputs to the action. Now, as of v2.9.0, you can use a glob pattern to specify the paths to the JaCoCo csv reports. This can actually now also work for the more common single module project, but the glob functionality is likely most useful in the multi-module case. Note that the CLI mode already implicitly supported globs since your shell will expand any globs you specify on the command line. But as a GitHub Action this previously was not the case as GitHub Actions doesn't expand globs in the inputs to an Action. The jacoco-badge-generator v2.9.0 now handles glob expansion internally.
This post focuses on workflows for the multi-module case, including introducing the new approach to using a glob to specify the JaCoCo reports. The README in the GitHub repository, as well as other posts in this series here on DEV, cover other functionality along with additional sample workflows.
Table of Contents: The remainder of this post is organized as follows:
Before you can use the jacoco-badge-generator, your workflow must generate a JaCoCo csv coverage report. Here are a couple examples of how to do this for different build tools.
Running JaCoCo with Maven
The example workflows in this post assume that you are using Maven to build and test a Java project, and that you have the jacoco-maven-plugin configured in your pom.xml in the test phase with something along the lines of the following:
Note that the workflows in the remainder of this post assume that Maven is in use, including Maven's default location and filename for the coverage report. You will need to alter the jacoco-csv-file input if you are using Gradle to the appropriate location and filename.
Workflow Specifying Paths to JaCoCo Reports Without Globs
This example workflow generates both badges (instructions coverage percentage and branches coverage percentage) for a multi-module project. The badges that are generated are computed over all modules. To do so, simply pass the paths to all of the JaCoCo reports that you want to include via the jacoco-csv-file input. The > is just Yaml's way of writing a string across multiple lines. You can also just list all on a single space-separated line, but your workflow file will be easier to read if you put them one per line. In this example, there are three subprojects: module1, module2, and module3. This workflow assumes that you are using Maven as your build tool, and that you have configured JaCoCo to run during the test phase.
name:buildon:push:branches:[main]jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3-name:Set up the Java JDKuses:actions/setup-java@v3with:java-version:'17'distribution:'adopt'-name:Build with Mavenrun:mvn -B test-name:Generate JaCoCo Badgeid:jacocouses:cicirello/jacoco-badge-generator@v2with:generate-branches-badge:truejacoco-csv-file:>module1/target/site/jacoco/jacoco.csvmodule2/target/site/jacoco/jacoco.csvmodule3/target/site/jacoco/jacoco.csv-name:Log coverage percentagerun:|echo "coverage = ${{ steps.jacoco.outputs.coverage }}"echo "branch coverage = ${{ steps.jacoco.outputs.branches }}"-name:Commit the badge (if it changed)run:|if [[ `git status --porcelain` ]]; thengit config --global user.name 'YOUR NAME HERE'git config --global user.email 'YOUR-GITHUB-USERID@users.noreply.github.com'git add -Agit commit -m "Autogenerated JaCoCo coverage badge"git pushfi
Workflow Specifying Paths to JaCoCo Reports With Globs
This next example workflow is just like the previous, except that now a glob is used to to find all JaCoCo reports named jacoco.csv regardless of where they appear within your project. This will actually also work if you have a single module project, with a single jacoco.csv but without the need to specify its path. We'll pass the glob pattern to the jacoco-csv-file input with the following: jacoco-csv-file: "**/jacoco.csv". Note that the quotes around the pattern are necessary. Without the quotes, the wildcard characters will lead to an "invalid workflow" error from GitHub Actions. We're essentially passing the glob as a string to the action, and the action then processes the glob internally.
name:buildon:push:branches:[main]jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3-name:Set up the Java JDKuses:actions/setup-java@v3with:java-version:'17'distribution:'adopt'-name:Build with Mavenrun:mvn -B test-name:Generate JaCoCo Badgeid:jacocouses:cicirello/jacoco-badge-generator@v2with:generate-branches-badge:truejacoco-csv-file:"**/jacoco.csv"-name:Log coverage percentagerun:|echo "coverage = ${{ steps.jacoco.outputs.coverage }}"echo "branch coverage = ${{ steps.jacoco.outputs.branches }}"-name:Commit the badge (if it changed)run:|if [[ `git status --porcelain` ]]; thengit config --global user.name 'YOUR NAME HERE'git config --global user.email 'YOUR-GITHUB-USERID@users.noreply.github.com'git add -Agit commit -m "Autogenerated JaCoCo coverage badge"git pushfi
Workflow Generating Separate Badges for Each Module
If you prefer to generate separate coverage badges for each of the modules of a multi-module project, then just include multiple steps of the jacoco-badge-generator in your workflow, such as in this example. Be sure to use the inputs to specify names for the badge files, otherwise with the defaults the subsequent steps will overwrite the previous. This example assumes that there are two modules. You also will likely want to use the coverage-label and branches-label inputs to change the text on the left side of the badges if you are displaying badges for multiple modules in the README of the same repository. This example demonstrates that as well.
name:buildon:push:branches:[main]jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3-name:Set up the Java JDKuses:actions/setup-java@v3with:java-version:'17'distribution:'adopt'-name:Build with Mavenrun:mvn -B test-name:Generate JaCoCo Badges for Module 1id:jacocoMod1uses:cicirello/jacoco-badge-generator@v2with:generate-branches-badge:truejacoco-csv-file:module1/target/site/jacoco/jacoco.csvcoverage-badge-filename:jacoco1.svgbranches-badge-filename:branches1.svgcoverage-label:coverage (module 1)branches-label:branches (module 1)-name:Generate JaCoCo Badges for Module 2id:jacocoMod2uses:cicirello/jacoco-badge-generator@v2with:generate-branches-badge:truejacoco-csv-file:module2/target/site/jacoco/jacoco.csvcoverage-badge-filename:jacoco2.svgbranches-badge-filename:branches2.svgcoverage-label:coverage (module 2)branches-label:branches (module 2)-name:Commit the badges (if they changed)run:|if [[ `git status --porcelain` ]]; thengit config --global user.name 'YOUR NAME HERE'git config --global user.email 'YOUR-GITHUB-USERID@users.noreply.github.com'git add -Agit commit -m "Autogenerated JaCoCo coverage badge"git pushfi
Multi-Module Examples with the CLI Utility
For those using the jacoco-badge-generator as a CLI tool, the following commands will accomplish the equivalent of the above three workflows. First, to install the CLI tool with pip from PyPI.
On Linux and MacOS:
python3 -m pip install jacoco-badge-generator
On Windows:
py -m pip install jacoco-badge-generator
Note that all examples below assume Linux (e.g., Python command is python3). If on Windows, just change python3 to py in all of the examples below.
Three Modules Without Globs:
Generate both instructions coverage and branches coverage badges for combination of three modules:
The jacoco-badge-generator can be used in one of two ways: as a GitHub Action or as a command-line
utility (e.g., such as part of a local build script). The jacoco-badge-generator parses a jacoco.csv
from a JaCoCo coverage report, computes coverage percentages
from JaCoCo's Instructions and Branches counters, and
generates badges for one or both of these (user configurable) to provide an easy
to read visual summary of the code coverage of your test cases. The default behavior directly
generates the badges internally with no external calls, but the action also provides an option
to instead generate Shields JSON endpoints. It supports
both the basic case of a single jacoco.csv, as well as multi-module projects in which
case the action can produce coverage badges from the combination of…
Features information on several open source GitHub Actions for workflow automation that we have developed to automate parts of the CI/CD pipeline, and other repetitive tasks. The GitHub Actions featured include jacoco-badge-generator, generate-sitemap, user-statistician, and javadoc-cleanup.
Vincent A. Cicirello - Professor of Computer Science at Stockton University - is a
researcher in artificial intelligence, evolutionary computation, swarm intelligence,
and computational intelligence, with a Ph.D. in Robotics from Carnegie Mellon
University. He is an ACM Senior Member, IEEE Senior Member, AAAI Life Member,
EAI Distinguished Member, and SIAM Member.