Automate Gradle Dependency Updates on Your Android Project

Managing Gradle dependencies on an Android project doesn’t have to be a hassle when you know how to leverage the Gradle Version Catalog and automate maintenance with a GitHub Action.

Modern Android projects usually include a lot of Gradle dependencies. Keeping them up-to-date at all times can sometimes be a difficult and time-consuming task.

A great solution for getting the upper hand in this situation is the Gradle Version Catalog – it serves as a central declaration of dependencies, and it’s useful for managing the dependencies on any Android project. 

This article will show you how to write a version catalog and include it in your Android project. It also gives an overview of the Version Catalog Update Plugin, which allows you to update the catalog automatically by checking all the dependencies included. 

Finally, we will help you automate this process using a GitHub Action and start a periodic check to keep all the dependencies up to date.

How Gradle Version Catalog works

The Gradle Version Catalog is a feature introduced in Gradle 7.0 that allows you to define and manage different versions of dependencies used in your Gradle builds. It provides a centralized way to declare and control the versions of external dependencies used in your project. With the Gradle Version Catalog, you can define a catalog file that lists the versions of your dependencies, plugins, and other artifacts.

Version catalogs can be declared in the settings.gradle.kts file, but the best way to take advantage of them is to use an external file named libs.versions.toml inside the gradle folder in the root of the Android project.

Here’s an example of a version catalog file:

	[versions]
android-gradle-plugin = "8.0.0"
gson = "2.10.1"
kotlin = "1.8.20"
lottie = "6.0.0"
material = "1.8.0"
room = "2.5.1"
timber = "5.0.1"

[libraries]
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
lottie = { module = "com.airbnb.android:lottie-compose", version.ref = "lottie" }
material = { module = "com.google.android.material:material", version.ref = "material" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }

[bundles]
room = [
    "room-ktx",
    "room-runtime",
]

[plugins]
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }

The catalog is divided into four parts:

Versions

Used for declaring the versions used in other sections.

Libraries

Several dependencies are declared using the previously declared versions.

Bundles

You can use it to combine more libraries in a single dependency.

Plugins

Focused on the plugins we can import into our Android projects.

Once the version catalog file is ready, it is possible to use Gradle dependencies in the build.gradle file.

To import a single dependency, we can use: 

	implementation(libs.lottie)

The bundles are useful for importing several logically related Gradle dependencies at once. In this example, the dependencies related to Room:

	implementation(libs.bundles.room)

The plugins can be used in the Gradle file as follows:

	plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.android.library) apply false
}

The file also accepts different version notations to highlight which version is preferred.

	[versions]
my-lib = { strictly = "[1.0, 2.0[", prefer = "1.2" }

There are various annotation options we can also use in the libraries section, but this is out of the scope of this article, and all the useful information can be found in the official documentation.

Keep your Gradle dependencies up to date with the Version Catalog Update Plugin

To update the dependencies of an Android project, you will need two different Gradle plugins:

The Version Catalog Update Plugin helps us keep all the versions in a Gradle version catalog TOML file up to date. This plugin uses the Gradle Versions Plugin to determine the version update.

First of all, we need to install the plugins:

	plugins {
  id "com.github.ben-manes.versions" version "0.41.0"
  id "nl.littlerobots.version-catalog-update" version "<latest version>"
}

Once the plugins are installed, to update the catalog file, you can execute the command ./gradlew versionCatalogUpdate at any time. This command handles the following tasks:

  • It updates any new versions available in the catalog while preserving existing version groups, if any.
  • It sorts the keys used in versions, libraries, bundles, and plugins by name (optional).
  • It updates bundles to include only valid keys and sorts the keys within a bundle.

The catalog will not receive any new entries, but any unused entries will be eliminated.

If you want to format the current libs.versions.toml file without making any changes to library versions, you can execute the command ./gradlew versionCatalogFormat. By doing this, the version catalog will be properly formatted, and new version references will be generated, similar to how the versionCatalogUpdate task functions. This is particularly useful when you’ve added an entry to the version catalog but are not yet prepared to update any gradle dependencies.

These two plugins come with a lot of configurations and options, which are well-described in GitHub repositories.

An important option that deserves mentioning is the logic that allows you to accept only stable versions.

You can create a function in the Gradle file in the following way:

	fun isNonStable(version: String): Boolean {
    val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
    val regex = "^[0-9,.v-]+(-r)?$".toRegex()
    val isStable = stableKeyword || regex.matches(version)
    return isStable.not()
}

Of course, it is a bit generic, but it can work in almost all cases to identify whether a dependency version is stable or not.

Once this function is present, it can be used as a filter on the task versionCatalogUpdate:

	tasks.named("dependencyUpdates").configure {
  rejectVersionIf {
    isNonStable(it.candidate.version)
  }
}

Automating Gradle dependency updates with GitHub Action

The process described in the previous two sections can easily be automated using a GitHub Action.

Let’s see an example of a GitHub Action that automates this process:

	name: Update Dependencies
 on:
   workflow_dispatch:
   schedule:
     - cron: '00 6 1 * *'
 jobs:
   update-dependencies:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v3
       - uses: actions/setup-java@v1
         with:
           java-version: 11
       - name: Grant execute permission for gradlew
         run: chmod +x gradlew
       - name: Perform dependencies resolution and update
         run: ./gradlew versionCatalogUpdate
       - name: Create Pull Request
         uses: peter-evans/create-pull-request@v4
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
           commit-message: update dependencies
           committer: GitHub noreply@github.com
           author: GitHub noreply@github.com
           title: Update dependencies
           body: |
             Dependencies update.
             Check the Version Catalog to see which dependencies need an update.
       branch: update-dependencies
       delete-branch: true
       labels: |
          dependencies update

First of all, the Action needs a name, and then you can set a schedule. In this case, it starts on the first day of every month at 6:00 AM.

The rest of the Action is basically composed by the project’s checkout. After that, the versionCatalogUpdate is invoked in a new update-dependencies branch, and finally, a pull request is created.

To create and customize the PR through a GitHub Action, we use the action peter-evans/create-pull-request@v4. All its configurations and options can be found in the official repository.

Leverage Version Catalog and GitHub Action for effortless Gradle dependency management

Managing Gradle dependencies in modern Android projects can be a challenging task, but utilizing a version catalog can greatly simplify the process. The Gradle Version Catalog provides a single source of truth so you can declare and control the versions of external dependencies used in your project. 

Additionally, with the help of the Version Catalog Update Plugin and the Gradle Versions Plugin, it’s easy to keep our Gradle Versions Catalog up to date with any newly available versions. These plugins automate the process of updating the catalog file by checking for new versions and sorting the entries. With the ability to format the catalog file without changing versions, it provides flexibility when adding new entries.

Finally, we can utilize GitHub Action to save time and effort by automating the update process. This way, we can create a pull request with all the changes automatically.

By leveraging the power of the Gradle Version Catalog, along with the Version Catalog Update Plugin and a GitHub Action, you can efficiently manage and keep your Android project’s dependencies up to date. This approach saves time and effort, ensuring that your project benefits from the latest features and bug fixes in the external libraries you depend on.