Keep your Gradle dependencies updated with GitLab issues

Daniel Mühlbachler
May 16 · 4 min read

A guide on how to use Gradle to keep your dependencies updated by creating GitLab issues for the development team.

Lately I have analysed a project of ours due to build issues with a newly added dependency and realized that whenever development teams are setting up their projects and define dependencies, most of them do not think about how to maintain and keep those updated. ⚠️💥
Not only will keeping them updated help with a lot of incompatibilities but can also prevent security vulnerabilities leaking from those libraries to your products. Hence, defining any kind of workflow to maintain to help teams in maintaining dependencies will provide value.

A quite simple, but ineffective, solution is to periodically create issues reminding developers about checking all dependencies. Unfortunately, this approach will not have the desired effect except for spamming teams with the same issue over and over again and, ultimately, annyoing developers. 😒
Based on that, I decided to start investigating on how to make sure outdated dependencies are reported and updated automatically, and periodically.

Looking at available Gradle plugins, one has to come across Ben Manes’ Versions plugin creating a JSON output like:

{
"current": {
"dependencies": [
{
"group": "com.github.ben-manes",
"version": "0.21.0",
"projectUrl": "https://github.com/ben-manes/gradle-versions-plugin",
"name": "gradle-versions-plugin"
}
],
"count": 1
},
"gradle": {
"current": {
"version": "5.4.1",
"reason": "",
"isUpdateAvailable": true,
"isFailure": false
},
"nightly": {
"version": "5.5-20190514000037+0000",
"reason": "",
"isUpdateAvailable": true,
"isFailure": false
},
"enabled": true,
"releaseCandidate": {
"version": "",
"reason": "update check succeeded: no release available",
"isUpdateAvailable": false,
"isFailure": false
},
"running": {
"version": "4.10.2",
"reason": "",
"isUpdateAvailable": false,
"isFailure": false
}
},
"exceeded": {
"dependencies": [

],
"count": 0
},
"outdated": {
"dependencies": [
{
"group": "javax.xml.bind",
"available": {
"release": null,
"milestone": "2.4.0-b180830.0359",
"integration": null
},
"version": "2.2.11",
"projectUrl": "https://github.com/javaee/jaxb-spec",
"name": "jaxb-api"
}
],
"count": 1
},
"unresolved": {
"dependencies": [
],
"count": 0
},
"count": 2
}

❕ Luckily, this plugin creates a report file (json, xml or plain text) and others have already built upon it to support their use cases, like automatically updating the versions or creating GitHub issues and Slack messages.
Although an automated update of dependencies sounds elegant, I soon realized this will only create more issues with incompatibilities, like deprecated API or transient dependencies, ultimately resulting in positioning the team of having to spend time on fixing them kind of immediately.
Anyhow, personally, I was quite fond of the idea to automatically create a GitLab issue listing the outdated dependency including the current and new version, similar to hellofresh’s deblibs plugin creating GitHub issues. This solution will allow us to focus on the product development while having ensured we get notified about outdated libraries and being able to update them as soon as we find time.

Unfortunately though, there was no implementation for creating GitLab issues and I quickly decided to implement a plugin myself following the approach of the deblibs plugin.


Besides the fact of testing a Gradle plugin, which I will write about in another story, developing the plugin in Java was quite straight forward and using GitLab’s well-documented API worked without any hassle. 👍
Moreover, once you get to know how to register the plugin, the configuration and tasks, everything else usually will not raise any issues for profound developers. Nevertheless, having nested configuration options was a bit tricky as, for example, I wanted to define a gitlab block for GitLab specific configuration like:

dependencyUpdateNotifier {
gitlab {
url = 'https://gitlab.com/api/v4'
projectId = 1
token = 'token'
label = 'label'
title = 'Dependency Updates (%count)'
}
}

What happens within Gradle when it tries to evaluate the gitlab block? It is using a Closure and, of course, the gitlab property in the extension class is not a Closure. Knowing this fact actually leads to a simple solution in defining a setter taking a Closure as its argument and initializing the property using Gradle’s built-in methods:

public class DependencyUpdateNotifierExtension {
private Project project;
private GitlabNotifierConfig gitlab;
GitlabNotifierConfig gitlab(final Closure closure) {
gitlab = new GitlabNotifierConfig();
project.configure(gitlab, closure);
return gitlab;
}

...
}

Voilá! 👏 Gradle parses the configuration correctly.

❓ So what does the new plugin do now?
Simply put, it is reading the generated JSON report by the Versions’ plugin and retrieves all open issues from the GitLab API to find all already reported dependency updates which ensures that as a developer I will not have to worry about getting spammed and seeing the same updates in each issue. Knowing this information a GitLab issue 📝 will be created listing our outdated dependencies including some key information.
Additionally, the plugin allows you to define labels 🚩 on created issues (which is also the filter criteria for retrieving open issues).

By configuring our Gradle build file properly (see example) we are now able to issue just one command for analyzing dependencies and reporting outdated ones to GitLab for our development team looking like this:

Created GitLab issue

🔎❗️️ You can find the plugin, its usage, documentation and examples on GitHub. 💃