How we stay up to date with our dependencies

Stefan M.
Level Up Coding
Published in
4 min readJun 30, 2020

--

I guess we all know how to add a dependency with Gradle, right? I mean, it’s basically a one-liner:

dependencies {
implementation("x.y.z:abc:1.0-alpha01")
}

Nothing new for us, right?

But what happens with that single line of code if there is a new version of the dependency available? Maybe the authors implemented a new feature, or better, fixed a bug!

Well, if you are like me or most people I know the following happen: Nothing. Or at least nothing will happen as long as you don’t need that new feature or a bug breaks something for you.

There is nothing wrong with doing this. It is totally fine to stay on a specific version as long as you’re happy with it (and the discovered bugs are irrelevant for your business). But wouldn’t it be cool if there is something to remind you about updates regularly? Not saying that you have to update but that there is the ability to update?

Recently my team and I wrote our own DependencyBumper. This tool regularly looks for updates of our dependencies and creates a pull request when one is found.

Before I go into detail. Yes, we are aware of Dependabot and its sibling tools. But most of them work only for a “standard Gradle setup” (whatever that means). Since we don’t have such a setup (and won’t change to one) we decided to build something on our own.

What does our current setup look like?

We have a file called versions.gradle where we define a Map with the “name of the dependency” as key and the Gradle string dependency notation as value. We put the whole map to the ExtraPropertiesExtension to make it available in all other build scripts.

ext {
deps = [:]
deps.kotlinjdk = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72"
}

We only have to apply this script in our build scripts to have access to the dependencies.

apply from: 'versions.gradle'dependencies {
implementation deps.kotlinjdk
}

How dependencies are updated in that file?

We make use of the Gradle versions plugin to check for updates. The provided task named dependencyUpdates will produce a JSON file that contains the dependencies which have an update.

Then we run a custom JavaExec task which will execute our DependencyBumper. The code does then the following:

  • Parses the JSON file to create a Map for each update
  • Create a new branch for each entry in the Map
  • Check if the branch name already exists at origin and skip this entry if so
  • Replaces (with the magic of Regex) the currently version in the versions.gradle with the new version.
  • Push the branch to origin
  • Create a pull request from this branch to our base branch develop

With the check if there is already a branch at origin we can simply close the pull request — if we are not interested in this update — but don’t delete the branch. Next time the DependencyBumper runs it will detect that the branch is already available and skip the part where it’s created the pull request.

A still open challenge for us is to update dependencies together which belongs together. As you see in the JSON example above, the dagger and dagger-compiler dependencies should be updated together. It doesn’t make any sense to have version 2.23 for dagger but version 2.24 for the dagger-compiler in a single code base. Currently, our tool creates a pull request for each dependency — which makes no sense.

That’s basically it. But — to be honest — when this topic raised the first time I was totally against this. I had the fear that it will force us to blindly merge these pull requests as long as our CI is green. Without taking a look at a changelog or release notes. Surprisingly, after the first round of updates (there were a lot ) I noticed the exactly opposite. We looked for all changes for each dependency and commented on mentionable changes in the pull request! This made the changes visible to the whole team which is not the case if a dependency is updated while working on a new feature or fixing a bug.

--

--