How to use Renovate.js and Gitlab-CI to keep your Javascript dependencies updated
2018-03-13 von Hannes
- Howto
- Tech
Why the effort
Mordern Javascript applications relay on many of 3rd party dependencies. Keeping everything updated can become a challenge when your application grows or you have to maintain multiple projects at a time. Further you have to take care of breaking changes or even rewrite large parts of your codebase, when a core depenencies has a new major release.
Like every developer, we have definitely struggled with this in the past and to be honest it can be a pain in the a$#. This becomes even a bigger problem when updating a lot of dependencies at the same time, which is usually the case when you decide to make updates after some time passed.
Instead updating dependencies should be a continuous process, where you update one dependency at a time and always relatively fast after an update is released.
This article we will show you how to get there!
How to fix this
Services like Gemnasium (accuired by Gitlab) help you to keep this problem under control. They watch your project dependencies (package.json in case of Javascript) and open a merge request when there is an update available. If your tests pass you can just merge it and you are up to date again. The key is that this process happens automatically and the developer just has to evaluate the change.
MUCH BETTER!
Since our projects live on a private self hosted Gitlab instance using these services is not an option for us. Luckily, I recently stumbled across Renovate, which looked very promising. After fiddeling a little bit around with it, I can say it's exactly what we needed. Renovate can open merge requests in our Gitlab containing all relevant information from the release notes.
Our Renovate Setup
We have created a new Project in Gitlab that will orchestrate the renovate tasks. Here is how to set it up.
-
Create a new Gitlab project. Let's call it fruehjahrsputz (german for spring cleaning). In our case it belongs to the group froehlichundfrei.
-
Setup a new Project with
yarn init
and add renovate as a dependency withyarn add renovate
. Also add a .gitignore file with the usual stuff to stay clean. -
Create a Docker Container to run the renovate tasks. This is what our Dockerfile looks like:
FROM node:8 ENV APP_HOME /app WORKDIR $APP_HOME # install dependencies ADD ./package.json ./yarn.lock "$APP_HOME/" RUN cd $APP_HOME && yarn install CMD [ "yarn", "renovate" ]
-
Create a new user in Gitlab to run the renovate tasks and create a Personal Access Token with the scope api. In our Gitlab Project we place this token as the Secret CI Variable GITLAB_TOKEN.
-
You should also create a Personal Access Token with the scope repos for your Github user that Renovate can use to check the packages. Otherwise you will hit Github's API limits very fast. Add this token as the Secret CI variable GITHUB_TOKEN.
-
Create the file repositories.txt that will contain the Gitlab repositories that should be checked regulary. Let's add this repository to get started:
froehlichundfrei/fruehjahrsputz
-
Create the script scripts/create-env-file.sh that will write our tokens to an .env file in order to access them inside the docker container:
#!/bin/bash set -e echo "GITHUB_TOKEN=\"${GITHUB_TOKEN}\"" > .env echo "GITLAB_TOKEN=\"${GITLAB_TOKEN}\"" >> .env # NOTE: Since our Gitlab instance uses a self signed certificate # we also add NODE_TLS_REJECT_UNAUTHORIZED here, so that renovate # ignores TLS rejections when talking to our gitlab instance. # You don't need this if you have a valid SSL Cert. echo 'NODE_TLS_REJECT_UNAUTHORIZED="0"' >> .env echo "Successfully created .env file"
-
Create a Makefile to run the common tasks for this project:
DOCKER_IMAGENAME := froehlichundfrei/fruehjahrsputz:latest DOCKER_CONTAINERNAME = fruehjahrsputz build: docker build -t $(DOCKER_IMAGENAME) . renovate: docker rm -f $(DOCKER_CONTAINERNAME) >> /dev/null || true docker run -i --name $(DOCKER_CONTAINERNAME) \ -v $(PWD):/app -v /app/node_modules \ $(DOCKER_IMAGENAME)
-
Create a .gitlab-ci.yml to run the CI tasks:
before_script: - pwd - sh scripts/create-env-file.sh after_script: - rm -f .env renovate: script: 'make build renovate' only: - master
-
Add the renovate script to the package.json of our project:
"scripts" : { "renovate" : "env $(cat .env | xargs) renovate --platform gitlab --endpoint https://<YOUR-GITLAB-DOMAIN>/api/v4 $(cat repositories.txt | xargs)" }
This will run Renovate against your Gitlab instance and should now open a merge request in our project called Configure Renovate after we commit and push all files. Very nice!
Setup the Gitlab Project
The new merge request describes everything that has to be done to setup our project. We need to add a renovate.json file to get going:
{
"assignees": ["hannes"],
"baseBranches": ["master"],
"extends": ["config:base"]
}
assignees - The user the merge requests will be assigned to (only one possible in Gitlab currently)
baseBranches - The branches that will be analysed
extends - Preset configs to use/extend
You can find all possible configuration options in the documentation. After adding this file, Renovate should start to check our project dependencies and open new merge requests when updates are available. These merge requests also contain the release notes from Github and an overview of all commits that have been made between your and the current version.
Very helpful to get a feeling if something will potentially 🔥 break 🔥 ...
Schedule a regular Execution in Gitlab-CI
Now, that we have everything setup correctly, we want to check the dependencies of all configured repositories on a regular basis, right?
This is where Gitlab's Pipeline Schedules come into play. We have created a schedule called Check for updates every 2 hours between 8am and 10pm UTC with a cron pattern of 0 8-22/2 * * *
.
This way we do NOT get notified during sleeping times 😴
Recap / What else?!
We realized very fast, that we are more confident to merge updates into our codebase, because smaller changes are easier to manage. Further we have better overview about the direction the projects we rely on are going and we see potential problems earlier. In addition, the use of merge requests enforces best practices in regard to the development process itself.
One issue we currently have is that Renovate does not seem to throw a non-zero exit code when it encounters errors (see #1338). So for now we have to check the output of the process from time to time to see if something is not working.
-> UPDATE 05.2018: Renovate now throws non zero exit codes when something goes wrong 🎉
Let us know if you like this article or have feedback!