As a DevOps Consultant, I often encounter codebases that have not been properly kept up-to-date. Likewise, I’ve authored many open-source projects on GitHub, which I use for training, presentations, and articles. Those projects often sit dormant for months at a time, #myabandonware.
Poorly maintained and dormant projects often become brittle or break, as their dependencies and indirect dependencies continue to be updated. However, blindly updating project dependencies is often the quickest way to break, or further break an application. Ask me, I’ve given in to temptation and broken my fair share of applications as a result. Nonetheless, it is helpful to be able to quickly analyze a project’s dependencies and discover available updates. Defects, performance issues, and most importantly, security vulnerabilities, are often fixed with dependency updates.
For Node.js projects, I prefer David to discover dependency updates. I have other favorites for Ruby, .NET, and Python, including OWASP Dependency-Check, great for vulnerabilities. In a similar vein, for Gradle-based Java Spring projects, I recently discovered Ben Manes’ Gradle Versions Plugin, gradle-versions-plugin. The plugin is described as a ‘Gradle plugin to discover dependency updates’. The plugin’s GitHub project has over 1,350 stars! According to the plugin project’s README file, this plugin is similar to the Versions Maven Plugin. The project further indicates there are similar Gradle plugins available, including gradle-use-latest-versions, gradle-libraries-plugin, and gradle-update-notifier.
To try the Gradle Versions Plugin, I chose a recent Gradle-based Java Spring Boot API project. I added the plugin to the gradle.build
file with a single line of code.
plugins { id 'com.github.ben-manes.versions' version '0.17.0' }
By executing the single Gradle task, dependencyUpdates
, the plugin generates a report detailing the status of all project’s dependencies, including plugins. The plugin includes a revision
task property, which controls the resolution strategy of determining what constitutes the latest version of a dependency. The property supports three strategies: release, milestone (default), and integration (i.e. SNAPSHOT), which are detailed in the plugin project’s README file.
As expected, the plugin will properly resolve any variables. Using a variable is an efficient practice for setting the Spring Boot versions for multiple dependencies (i.e. springBootVersion
).
ext { springBootVersion = '2.0.1.RELEASE' } dependencies { compile('com.h2database:h2:1.4.197') compile("io.springfox:springfox-swagger-ui:2.8.0") compile("io.springfox:springfox-swagger2:2.8.0") compile("org.liquibase:liquibase-core:3.5.5") compile("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2") compile("org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}") compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}") compile("org.springframework.boot:spring-boot-starter-data-rest:${springBootVersion}") compile("org.springframework.boot:spring-boot-starter-hateoas:${springBootVersion}") compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}") compileOnly('org.projectlombok:lombok:1.16.20') runtime("org.postgresql:postgresql:42.2.2") testCompile("org.springframework.boot:spring-boot-starter-test:${springBootVersion}") }
My first run, using the default revision level, resulted in the following output. The report indicated three of my project’s dependencies were slightly out of date:
> Configure project : Inferred project: spring-postgresql-demo, version: 4.3.0-dev.2.uncommitted+929c56e > Task :dependencyUpdates Failed to resolve ::apiElements Failed to resolve ::implementation Failed to resolve ::runtimeElements Failed to resolve ::runtimeOnly Failed to resolve ::testImplementation Failed to resolve ::testRuntimeOnly ------------------------------------------------------------ : Project Dependency Updates (report to plain text file) ------------------------------------------------------------ The following dependencies are using the latest milestone version: - com.github.ben-manes.versions:com.github.ben-manes.versions.gradle.plugin:0.17.0 - com.netflix.nebula:gradle-ospackage-plugin:4.9.0-rc.1 - com.h2database:h2:1.4.197 - io.spring.dependency-management:io.spring.dependency-management.gradle.plugin:1.0.5.RELEASE - org.projectlombok:lombok:1.16.20 - com.netflix.nebula:nebula-release-plugin:6.3.3 - org.sonarqube:org.sonarqube.gradle.plugin:2.6.2 - org.springframework.boot:org.springframework.boot.gradle.plugin:2.0.1.RELEASE - org.postgresql:postgresql:42.2.2 - org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2 - org.springframework.boot:spring-boot-starter-actuator:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-data-jpa:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-data-rest:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-hateoas:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-test:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-web:2.0.1.RELEASE The following dependencies have later milestone versions: - org.liquibase:liquibase-core [3.5.5 -> 3.6.1] - io.springfox:springfox-swagger-ui [2.8.0 -> 2.9.0] - io.springfox:springfox-swagger2 [2.8.0 -> 2.9.0] Generated report file build/dependencyUpdates/report.txt
After reading the release notes for the three available updates, and confident I had sufficient unit, smoke, and integration tests to validate any project changes, I manually updated the dependencies. Re-running the Gradle task generated the following abridged output.
------------------------------------------------------------ : Project Dependency Updates (report to plain text file) ------------------------------------------------------------ The following dependencies are using the latest milestone version: - com.github.ben-manes.versions:com.github.ben-manes.versions.gradle.plugin:0.17.0 - com.netflix.nebula:gradle-ospackage-plugin:4.9.0-rc.1 - com.h2database:h2:1.4.197 - io.spring.dependency-management:io.spring.dependency-management.gradle.plugin:1.0.5.RELEASE - org.liquibase:liquibase-core:3.6.1 - org.projectlombok:lombok:1.16.20 - com.netflix.nebula:nebula-release-plugin:6.3.3 - org.sonarqube:org.sonarqube.gradle.plugin:2.6.2 - org.springframework.boot:org.springframework.boot.gradle.plugin:2.0.1.RELEASE - org.postgresql:postgresql:42.2.2 - org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2 - org.springframework.boot:spring-boot-starter-actuator:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-data-jpa:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-data-rest:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-hateoas:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-test:2.0.1.RELEASE - org.springframework.boot:spring-boot-starter-web:2.0.1.RELEASE - io.springfox:springfox-swagger-ui:2.9.0 - io.springfox:springfox-swagger2:2.9.0 Generated report file build/dependencyUpdates/report.txt BUILD SUCCESSFUL in 3s 1 actionable task: 1 executed
After running a series of automated unit, smoke, and integration tests, to confirm no conflicts with the updates, I committed my changes to GitHub. The Gradle Versions Plugin is a simple and effective solution to Gradle dependency management.
All opinions expressed in this post are my own, and not necessarily the views of my current or past employers, or their clients.
Gradle logo courtesy Gradle.org, © Gradle Inc.