Keeping Gradle Dependencies up to Date with GitHub Actions and RefreshVersions

How to use GitHub Actions to semi-automate dependency updates using RefreshVersions

Updating dependencies is work that no one wants to do, that's why automating it is a crucial part of maintaining a healthy code base. Updates, among other things, improve developer experience, fix bugs or make the app more secure.

Sometimes dependencies have major updates, which usually require migrations, because their user-facing API changes. These migrations can be problematic by themselves, and the complexity only increases when there are several major updates at the same time.

Ways of checking for dependency updates

There are numerous ways for to do this, but I'll describe the three I'm most familiar with.

Manually

Checking for new versions by hand is the most time-consuming way of doing it and also the most error-prone, because it introduces a human into the whole process.

Gradle versions plugin

This plugin is the easiest way for automating dependency update checks. It can generate the report in various formats, which can then be for example uploaded somewhere. However, I've found that the reports can get huge and contain some unneeded information.

Updating the dependency, requires copying the new version from the report and pasting it into gradle.

RefreshVersions

This tool requires some set-up upfront, because it changes how gradle versions are defined in the project. This set-up however can be semi-automated with the refreshVersionsMigrate task.

RefreshVersions uses a versions.properties file which holds all the dependency versions. When a new version will be available, it will be added as a comment to this file.

version.androidx.compose.foundation=1.0.4
##                      # available=1.0.5

Updating the dependency only requires updating the number using the one from the comment. The full documentation for the refreshVersions can be found here.

Automating it with GitHub Actions

Because RefreshVersions doesn't update the dependencies by itself, a developer still needs to do the actual update. It would be nice to automate the process which comes before updating the dependencies.

An example for how this could be automated is creating a draft pull request with the updated versions.properties, so the developer only has to change the versions.

The steps for such a job could look like this:

  • Update the versions.properties file
  • Create a commit with the update
  • Push the commit to a branch
  • Create a draft PR

The above steps should be ideally executed automatically without user input, for example on a weekly or monthly schedule.

The GitHub Actions Workflow file:

name: RefreshVersions PR

on:
  workflow_dispatch:
#  uncomment for weekly run
#  schedule:
#    - cron: '00 07 * * 1'

jobs:

  Refresh-Version:
    name: Run the refresh version
    runs-on: ubuntu-latest
    env:
      MAIN_BRANCH: "main"
      DEPENDENCY_UPDATE_BRANCH: "dependency-update"
      
    steps:
      - uses: actions/checkout@v2
        with:
          ref: ${{ env.MAIN_BRANCH }}

      - uses: actions/setup-java@v2
        with:
          distribution: "adopt"
          java-version: "11"

      - uses: peterjgrainger/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          branch: ${{ env.DEPENDENCY_UPDATE_BRANCH }}

      - name: RefreshVersions
        run: ./gradlew refreshVersions

      - uses: EndBug/add-and-commit@v7
        name: Commit
        with:
          author_name: "GitHub Actions"
          author_email: "[email protected]"
          branch: ${{ env.DEPENDENCY_UPDATE_BRANCH }}
          message: "Refresh versions.properties"
          # Force pushing will prevent errors when the branch is not removed
          # push: "--force"


      - uses: repo-sync/pull-request@v2
        name: Pull Request
        with:
          source_branch: ${{ env.DEPENDENCY_UPDATE_BRANCH }}
          destination_branch: ${{ env.MAIN_BRANCH }}
          pr_draft: true
          pr_title: "Update gradle dependencies"
          github_token: ${{ secrets.GITHUB_TOKEN }}

The above workflow can be seen in action in this sample repository:

GitHub - AKJAW/refreshVersions-github-actions
Contribute to AKJAW/refreshVersions-github-actions development by creating an account on GitHub.

It created the following pull request:

Now all that's left is to checkout to this branch and update the versions.properties file with the available versions.

Thank you for reading, let me know if you use a different strategy for keeping dependencies up to date.

Course

If you'd like to learn more about Android Ci/CD, or about more outside-coding processes, checkout the Android Next Level course which I'm a co-author of.

Android Next Level
A course about processes outside of coding like: CI/CD, scaling your project, good git hygiene or how to cooperate with your teammates
You've successfully subscribed to AKJAW
Great! Now you have full access to all members content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.