Keeping Gradle Dependencies up to Date with GitHub Actions and 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/action-create-branch@v2.0.1
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: "noreply@github.com"
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:
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.