Let’s suppose you have a big project, which contains multiple dependencies. Some of them are created by you and they are hosted in separate GitHub repositories. These dependencies have their own dependencies and from my experience, in most cases you forget to maintain them up to date. It becomes a big problem if there’s any bug inside this dependency and you have to find a suitable combination of the inner dependencies to make the dependency build again. Here’s the way how I solved this issue for myself.
Part 1 — Integrate Renovate inside the main project
Renovate is a service that provides a bot which is integrated in your, for example, GitHub repository, and does the following:
- Scans your repositories to find package files and their dependencies
- Checks if any newer versions exist
- Raises Pull Requests for available updates
Here’s the docs about how it can be integrated into your repository. Spoiler — there’s nothing difficult in it. After this step our main repository would start to receive the updates of dependencies.
Part 2 — Create a GitHub action to build (and test) the PR with update inside the dependency repository
For me the most suitable solution is to use GitHub Actions as a CI tool since it provides unlimited usage for open-source projects and it works in cloud. Here’s the script I use to build the PR:
name: Validate PR | |
on: | |
pull_request: | |
branches: | |
- 'master' | |
jobs: | |
build_project: | |
if: startsWith(github.head_ref, 'renovate') | |
runs-on: macos-latest | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4.1.1 | |
- name: Setup java | |
uses: actions/setup-java@v4 | |
with: | |
distribution: 'temurin' | |
java-version: '17' | |
- name: Build | |
run: | | |
./gradlew :dependency:build --no-daemon |
As you can see, script is pretty simple — it builds only PR’s into master branch from the branches, which name starts with renovate. You can use any system image you need, since I build KMP libraries, I need to use the macOS. Then the machine downloads the repository, setups Java and just builds the artefact (in case you have tests you can run them too). If this stage is finished successfully the dependency is ready to merge.
Part 3 — Integrate the Renovate inside the dependency project
There’s nothing interesting at this stage, it is the same as Part 1, but in another repository.
Part 4 — Create a GitHub action to build and publish the new version of the dependency
I created the following action for it:
name: Publish new version | |
on: | |
push: | |
branches: | |
- 'master' | |
jobs: | |
build_project: | |
runs-on: macos-latest | |
env: | |
GITHUB_USER: ${{ secrets.GITHUB_USER }} | |
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4.1.1 | |
with: | |
fetch-tags: true | |
fetch-depth: 0 | |
- name: Setup java | |
uses: actions/setup-java@v4 | |
with: | |
distribution: 'temurin' | |
java-version: '17' | |
- name: Set version | |
run: | | |
VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) | |
major=$(echo $VERSION | cut -d'.' -f1) | |
minor=$(echo $VERSION | cut -d'.' -f2) | |
fix=$(echo $VERSION | cut -d'.' -f3) | |
echo "LIBRARY_VERSION=$major.$minor.$(($fix + 1))" >> "$GITHUB_ENV" | |
- name: Build and publish | |
run: | | |
./gradlew buildAndPublish --no-daemon | |
- name: Create tag | |
uses: actions/github-script@v7.0.1 | |
with: | |
github-token: ${{ env.GITHUB_API_KEY }} | |
script: | | |
github.rest.git.createRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: 'refs/tags/${{ env.LIBRARY_VERSION }}', | |
sha: context.sha | |
}) |
Let me explain what this action does. It triggers on each push into master. Then it sets the environmental variables from secrets (I need them to publish the artifact). Next two steps are similar to the previous script. Stage Set version is quite interesting — it fetches the latest tag (which has format minor.major.fix, i.e. 1.2.3) and increments fix version. It allows to create an updated version number. Note — presumably I had to create the first tag manually. Build and publish stage invokes buildAndPublish task, which combines two tasks inside — build and publish, as you can understand :). After the artefact is published, the latest stage is invoked — create new tag in GitHub repository (i.e. 1.2.4).
Part 5 — Handle the PR with dependency update in the main project
When the new version of the dependency is published, Renovate creates a PR with its’ update inside the main repo and here you are free to validate it as you wish.
So, as a conclusion, let’s describe an old and new processes.
Old process was the following: look for the inner dependencies update (manually) -> validate them (manually) -> merge into main branch of the dependency project (manually) -> publish new version (manually) -> validate new dependency version in the main project.
New process looks like this: look for the inner dependencies update (automatically, by Renovate) -> validate them (automatically, by GitHub Action) -> merge into main branch of the dependency project (manually) -> publish new version (automatically, by GitHub Action) -> validate new dependency version in the main project.
I hope that this article increases your development speed!
Thank you for reading! Feel free to ask questions and leave the feedback in comments or Linkedin.
This article was previously published on proandroiddev.com