How to automatically check dependency licenses in Gradle projects using Gradle License Report

Learn how to perform an automatic license check on your Gradle dependencies and avoid having bigger troubles because of license infringement.

Image Source: https://fyi.extension.wisc.edu/news/2016/06/02/credit-reports-important-for-singles-married-couples/

In software development, it’s not uncommon to introduce a new dependency to a project to solve a specific problem. And it is easy and common to forget to check the license of the dependency you are adding to your project. But forgetting it might cause a lot of trouble to you and your company. For instance, you might unknowingly add a library that has an AGPL license, which demands you to disclose and distribute your full source code and make it open source. You or your company might not be willing to do such a thing with your commercial, closed-source project. Not complying with the terms of the license, you and your company can easily run into legal issues because of software license infringement. Fortunately, there’s a plugin called “Gradle License Report” that can prevent having legal issues and save us time by automatically comparing the licenses of the dependencies to the set of licenses you or your company allows. This automated task can also be easily integrated into your CI pipeline and be potentially applied as a company-wide solution.

Goal

Our goal by using this plugin is to add an additional step to our ./gradlew check task — which comes as a part of Gradle Java plugin — that will go through each of our dependencies and compare their licenses with the set of licenses that we allow in our project.

Let’s Get Started!

We can start using it by simply adding the following to our build.gradle file:

plugins {
id 'com.github.jk1.dependency-license-report' version '1.16'
}

Then, we need to create a file allowed-licenses.json in the following format, which will contain the set of licenses we allow. For simplicity, we only have Apache License, Version 2.0, and Eclipse Public License v1.0 in our list.

{
"allowedLicenses": [
{
"moduleLicense": "Apache License, Version 2.0"
},
{
"moduleLicense": "Eclipse Public License - v1.0"
}
]
}

License Normalizer

We also need to use a normalizer, which normalizes the license names written in different variations. For instance, one dependency’s license might be specified as Apache License Version 2.0 , and another one might be Apache License, Version 2.0 . Without a normalizer, we would have to add each of these two to our allowed-licenses.json file separately, although they imply the same license. Therefore, using a normalizer saves us from having redundancy issues and allows us to maintain a cleaner allowed licenses list.

To do so, we need to create a new JSON file license-normalizer-bundle.json in which we can specify our transformation rules based on license name, license URL, and license file content, in the following format:

{
"bundles": [
{
"bundleName": "Apache-2.0",
"licenseName": "Apache License, Version 2.0",
"licenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"bundleName": "EPL-1.0",
"licenseName": "Eclipse Public License - v 1.0",
"licenseUrl": "http://www.eclipse.org/legal/epl-v10.html"
}
],
"transformationRules": [
{
"bundleName": "Apache-2.0",
"licenseNamePattern": ".*The Apache Software License, Version 2\\.0.*"
},
{
"bundleName": "Apache-2.0",
"licenseNamePattern": "Apache[ |-|_]2.*"
},
{
"bundleName": "Apache-2.0",
"licenseNamePattern": "ASL 2\\.0"
},
{
"bundleName": "Apache-2.0",
"licenseNamePattern": ".*Apache License,?( Version)? 2.*"
},
{
"bundleName": "Apache-2.0",
"licenseUrlPattern": ".*(www\\.)?opensource\\.org/licenses/Apache-2\\.0.*"
},
{
"bundleName": "Apache-2.0",
"licenseUrlPattern": ".*www\\.apache\\.org/licenses/LICENSE-2\\.0.*"
},
{
"bundleName": "Apache-2.0",
"licenseFileContentPattern": ".*Apache License,?( Version)? 2.*"
},
{
"bundleName": "EPL-1.0",
"licenseNamePattern": "Eclipse Public License.*(v|version)\\.?\\s?1(\\.?0)?"
}
]
}

In the normalizer example above, bundles list defines how the normalized license names and URLs should look, and transformationRules list contains the transformation rules defined for our bundles. The items in bundles and transformationRules are matched based on bundleName . As a starting point, you can use default-license-normalizer.bundle.json provided in the plugin’s GitHub repository, which normalizes a set of frequently used licenses.

Configuring build.gradle

And lastly, we need to do some additional configurations in our build.gradle file:

check {
dependsOn checkLicense
}
import com.github.jk1.license.filter.LicenseBundleNormalizer
import com.github.jk1.license.render.CsvReportRenderer
apply plugin: 'com.github.jk1.dependency-license-report'licenseReport {
configurations = ALL
allowedLicensesFile = new File("$projectDir/config/allowed-licenses.json")
filters = [new LicenseBundleNormalizer(bundlePath: "$projectDir/config/license-normalizer-bundle.json")]
renderers = [new CsvReportRenderer()]
}

With the configurations above, we make check task depend on checkLicense task that comes with Gradle License Report plugin. By setting configurations to ALL , license check will be done for all types of dependencies, including test dependencies. We need to specify the paths for the JSON files that we just created using allowedLicensesFile and filters fields. In this example, it is assumed allowed-licenses.json and license-normalizer-bundle.json are placed in a folder named config located in the project’s root folder, so make sure to adjust the paths according to your project structure.

Report Renderer

As you might have noticed, we also added a CSV report renderer to our configuration, which is useful when generating a license report from our dependencies. To generate a license report, you can simply run ./gradlew generateLicenseReport . The result will be available as a CSV, in your build/reports folder. You can also use other types of renderers, such as SimpleHtmlReportRenderer , JsonReportRenderer etc.

In Action

And that was all we had to do with our project to configure the license checker. When you run ./gradlew check or any task that depends on check, license check will be performed automatically via checkLicense, and if the license check fails, check task will fail because of checkLicense task.

The set of dependencies that caused checkLicenseto fail will become available in dependencies-without-allowed-license.json file located in build/reports folder, so that you can take a look and see if you want to do some adjustments.

And that’s it! As a side note, if you want to know more about what the licenses of your dependencies actually mean without getting lost in the legal language, I would recommend tldrlegal.com.

Thank you for reading my first Medium article! Please let me know if you have any questions or any feedback in the responses section.

Software Engineer at Tenera | BCG Digital Ventures