How to use Renovate.js and Gitlab-CI to keep your Javascript dependencies updated
2018-03-13 von Artikel von HannesHannes

  •  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 (recently 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.

  1. Create a new Gitlab project. Let's call it fruehjahrsputz (german for spring cleaning). In our case it belongs to the group froehlichundfrei.

  2. Setup a new Project with yarn init and add renovate as a dependency with yarn add renovate. Also add a .gitignore file with the usual stuff to stay clean.

  3. 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" ]
    
  4. 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.

  5. 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.

  6. 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
    
  7. 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"
    
  8. 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)
    
  9. 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
    
  10. 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!

Kommentare