Bring In The Bots, And Let Them Maintain Our Code!

Written by patrickleet | Published 2020/01/23
Tech Story Tags: nodejs | automation | software-development | software-engineering | testing | code-coverage | confidence | programming

TLDR Learn how to automate away as much maintenance as possible using Renovate, Travis CI, and CodeCov. Use a particularly useful bot called "Renovate" which will keep track of the project's dependencies and make Pull Requests every time an update needs to be made. At the end of this article we will explore what extra steps could be taken to give us even more confidence in each change. We'll start merging changes that could potentially break our project's code coverage with Travis CI.via the TL;DR App

Learn how to automate away as much maintenance as possible using Renovate, Travis CI, and CodeCov

Last year I wrote a five part series of articles that built a React boilerplate using Parcel. It demonstrated how to use streaming server side rendering, automatically enforce code quality, achieve 100% code coverage, explored using Docker for development, and covered multi-stage Dockerfiles for production.
Unfortunately, as time has passed, the boilerplate has begun to get stale. Some of the libraries I chose have changed their APIs, and as a result people following the tutorials as written have encountered some errors as they are getting later versions when following along and typing
npm i
.
It's come time to update this boilerplate, but in my typical fashion, I've decided to turn it into a learning experience by showing how to let bots do the heavy lifting for me.
In this article we will use a particularly useful bot called "Renovate" which will keep track of the project's dependencies and make Pull Requests every time an update needs to be made. Renovate was recently acquired by WhiteSource and was previously named RenovateBot. Because we have a bunch of automated tests and code quality enforcement at each step of the way we should be able to quickly see if everything is still passing or if a particular update causes any breaks.
At the end, we will also explore what extra steps could be taken to give us even more confidence in each change.
With that being said, here is the repo we will be renovating: https://github.com/patrickleet/streaming-ssr-react-styled-components
Let's get started.

Step 1: Enable Renovate

My repository is on Github, and therefore we will head over to the Github Marketplace where we can enable the Renovate Application. According to their website, there is also support for GitLab, as well as a tool that can be run with some manual set up in other situations such as On Premises. I've been using Renovate Bot for over a year now, with great success, but I've only tried the GitHub version.
Developer time is expensive, best to let bots do as much as possible!
The setup is pretty straightforward, just press "Install it for free" down at the bottom. If you need more help, I defer to checking their website, as the process may change after this article is written.

Step 2: Configure Renovate

Once Renovate has been installed, you can configure it from the "Settings" page of your account or organization in the "Applications" tab.
You'll have the choice to enable it for all of your repositories, or select repositories. Seems how I have a lot of repos, I decided to only enable it for certain ones. This choice is up to you.
Shortly after giving Renovate access to your repository you should receive a Pull Request titled "Configure Renovate" which will add a file named renovate.json to your project.
If we take a look inside we can see that the config is pretty simple, but allows for lots of customization.
{
  "extends": [
    "config:base"
  ]
}
Some customizations you may want to make are things like enabling automerge if you have a high enough confidence in your automated test coverage and code quality enforcement. This will allow renovate to automatically update packages for you if all checks pass. It's also possible to only enable automerge for minor versions, just devDependencies and much more. I'll leave it to you to look through all of the configuration options and decide what is best for your team and project.
In this initial Pull Request, Renovate will detail which package files it has detected and what Pull Requests with what updates you can expect to see once it's been merged.
One of the reason's I switched from using Greenkeeper to Renovate is it's support for much more than Node.js packages. For our purposes, it will also keep Dockerfiles, docker-compose, and Helm chart files up to date, but also supports much more such as Gradle. The full list can be found in the project's documentation.
To finish the configuring your repository for Renovate simply merge the Pull Request.

Step 3: Automatically Run Tests For Every Pull Request with Travis CI

In the previous articles we set up a bunch of Code Quality enforcement as well as configured automated tests with 100% code coverage. Before we start merging changes that could potentially break our project, we'll want to make sure that all of these checks are run with every Pull Request.
There are many options to do this, but the quickest and easiest for open source projects is probably Travis CI.
Enabling Travis CI is just as easy as Renovate, just head on over to the Application in the Github Marketplace, select the free "Open Source" version, and press Install.
Again, you'll have the option to enable it for all repositories, or select repositories. I'm again choosing to limit it to just my selected repos.
In order for Travis to kick in, we'll need to add a
.travis.yml
file to the project with the appropriate settings. The minimum we will need is to set the language of the project. Supported languages can be found in their docs.
language: node_js
Travis notices right away and starts running our builds.
By default, Travis CI does not know what version of Node we are supposed to be running, however, and uses the default version of
0.10.48
which is probably about a decade old by now. We will need to make a small update to our config file to tell it what we want instead. At the time of writing this article Node 11 was the latest version, and was chosen.
Let's go ahead and tell Travis to run the build in Node 11, as well as the latest version 12.
language: node_js
node_js:
  - 11
  - 12
Travis knows to run tests as part of its default Node.js script, and for many libraries or applications, this will be enough. This particular application, however, runs some tests against the built version that is outputted by Parcel, and for that reason, we will need to customize the script to make sure our Parcel build runs before the tests.
Let's update our
.travis.yml
file again to account for this build step before the script runs.
language: node_js
node_js:
  - 11
  - 12

before_script:
  - npm run build
And with that, we have passing builds!

Step 4: Enable Code Coverage Reporting

Alright, we have passing builds, and our tests are outputting code coverage, but it's a bit hard to get at. We want to ensure that we are not losing coverage with a new PR, so it'd be really helpful if we had that information as part of the Pull Request. For this, we can use a tool called CodeCov from the Github Marketplace.
Just as before, select the "Open Source" plan and press Install.
Again, I've chosen to only give permission for specific repositories, as I have a lot.
In order to finish setting up CodeCov we can add a couple of steps to our
.travis.yml
file. We can do this in the
after_success
lifecycle step.
Here's our new
.travis.yml
file:
language: node_js
node_js:
  - 11
  - 12

before_script:
  - npm run build

after_success:
  - npm i -g codecov
  - codecov
Now with each PR, a bot will comment with changes in coverage. Here's the PR where CodeCov was enabled.
The question marks are simply because we didn't have coverage data in the master branch yet. Once this PR is merged, future report will include the difference between the PR and the master branch.

Step 5: Pin Dependencies

While we were setting up Travis and CodeCov, Renovate made some Pull Requests. The first of which was setting all of the dependencies to a fixed version. This eliminates the reliance on SemVer, or semantic versioning. SemVer allows a range of versions and can be useful to get minor and patch updates each time you run
npm install
. Seems how we have a bot monitoring our packages for us, we will rely on it to do this work instead of SemVer. This results in more reproducible builds and less possible variance between each developer's machine. A more detailed discussion of the reasoning behind this can be found in Renovate's blog.
We'd like to see how each of the PRs affect test coverage and ensure they are still passing, though. Renovate is pretty smart, and can easily rebase the PR's it has made. Given enough time it will usually do so automatically, but we can help it along by clicking the checkbox in the PR's description that says "If you want to rebase/retry this PR, check this box".
Within a few moments a rebase has been performed at the Travis build kicks off, and a couple of minutes later we have passing builds and tests, as well as a code coverage report showing no change in coverage - still at 100%.
Pinning dependencies is a rather safe operation, as all that is happening is the explicit versions that are already installed in the package-lock.json file are being reflected in package.json. We can also see all of the tests are still passing, and code coverage has not been affected.
Let's merge it!

Step 6: Testing our Dockerfiles

If you're paying close attention, you've noticed that we are testing our Node application, but not our Dockerfiles. In the previous article we created a process to run and test our Dockerfiles but we are not taking advantage of that process in Travis. The problem is that Renovate will make PRs to keep our Dockerfile's up to date, and if we don't automatically run them we may miss something that works on Travis but not in the container.
Let's fix that. To do so we can add a new section to our
.travis.yml
file called jobs that define the docker build jobs.
language: node_js
node_js:
  - 11
  - 12

before_script:
  - npm run build

after_success:
  - npm i -g codecov
  - codecov

jobs:
  include:
    - services:
        - docker
      script:
        - docker build . -t ssr
    - services:
        - docker
      script:
        - docker build . -f nginx/Dockerfile -t nginx
We have two Dockerfiles, because this project specifically demonstrates two different alternatives - one with SSR, and one with Nginx. Therefore, we have two new jobs defined.
In the Travis UI we can see that we now have four jobs – the original Node 11 and Node 12 builds, as well as two more that run the
docker build
commands.
Clicking inside of "25.3" and "25.4" we can see the output for each of the docker build commands indicating their success.

Step 7: Package updates

With the pinned dependencies out of the way, we will start receiving Pull Requests for our other packages that need updating. The updates will either be a minor version update, which are generally pretty safe so long as the package author is using semver appropriately, or major version updates, which should receive a bit more scrutiny. Make sure if you had any open before the Travis and CodeCov changes you trigger a rebase of those branches. We'll also get updates to our Dockerfiles, and thanks to the last step, we know those are being built as well.
Renovate will only make two Pull Requests per hour, and not open more than twenty total pull requests at a time.
Let's take a look at the PRs I've received so far, and talk about how to handle each of them.
There are five of them so far, and a range of different types of examples. #9 and #11 are both update to Dockerfiles, and they are both passing, including actually running the builds so we know that the updates still result in a working Dockerfile. It'd be nice to run them as well and have a preview environment created automatically for each environment, but we'll need more tools and a more complicated set up to get that. It's totally possible, and something I usually set up for my clients, but this article is getting long already.
#12 is a major version update. In version 1 of react-helmet-async there is a breaking change, and our 100% code coverage correctly detected that something went wrong. If automerge were enabled, this would not have made it through, exactly as planned.
#14 is a major version update as well, but our build and tests are working, so we can be pretty confident we are all good here. Again, a preview environment that actually runs this build in the cloud for us would get us to that next level of confidence. We can click the down arrow next to "Release Notes" in the PRs description to take a deeper look.
If an author is diligent they should detail what breaking change warranted the major version bump. Unfortunately in this case, the author did not provide this information. Not knowing what the reason was increases the risk, and decreases developer confidence. If you're a library author, I strongly suggest using tools like commitizen and semantic-release to make sure users of your package are kept informed. I wrote up a process of how to do this in a previous article: These 6 essential tools will release, version, and maintain your NPM modules for you.
Lastly, #16 is a minor version update, but demonstrates Renovate's ability to group related packages. Some libraries choose to version all components of the library together. Renovate contains some preset, popular libraries in this manner, such as babel as we are seeing here.
I like living on the wild side, and hate clicking buttons that a bot could click for me, so instead of going through and merging all of these, I'm just gonna go ahead and enable automerge for Renovate. I'm pretty confident that the 100% coverage of the tests do a good job in catching breaking changes as they have demonstrated with PR #12.
To enable automerge, update your
renovate.json
file like so:
{
  "extends": [
    "config:base"
  ],
  "automerge": true
}
However, without preview environments, there's too much that could potentially go wrong with major versions. Let's make one more update so major versions will require a manual review and merge:
{
  "extends": [
    "config:base"
  ],
  "automerge": true,
  "major": {
    "automerge": false
  }
}
Automerge will take some time to kick in, so I'll let it do it's thing for awhile.
While my bots get to work, let's take a look at the failing PR and see if we can find out what went wrong there.

Step 8: Investigating failing builds

The failing build is caused by updating the library "react-helmet-async" to from version 0.2.0 to 1.0.0. Unfortunately, when we expand the release notes, the library author did not give us any information about what the breaking change may have been.
Renovate does allow us to click through via the "Compare Source" link to see what commits and code changes have occurred though, and of course, looking at the failing tests may give show us what went wrong.
From clicking Compare Source for V1.0.0 we can see that the breaking change is moving the default export to a named export.
The error message in the tests was pretty unclear, so thankfully looking at the "Compare Source" link helped us find the change pretty quickly.
To fix it, I'm going to go ahead and check out the branch that Renovate made and simply add a new commit to it which changes the import to use the new named import. Here's the commit containing the fix.
And with that, our tests are now passing again.

Step 9: Testing major version changes

PR #14 is a great example of why we need to test major version changes. If we had a preview environment we'd find that although the tests are all passing, there are run time errors related to
react-imported-component
API changes. Seems how we do not, I had to discover this by checking out the branch that renovate created and running it manually.
The library has changed in order to be inline with React Suspense and React.lazy. Looking through the docs shows us how to refactor to use the new API. If you're interested, the diff can be seen here.

Conclusion

Developer time is expensive, and maintenance is important as package updates often contain security fixes. Staying up to date will make sure any known bugs in the packages you are using get fixed as the author makes updates. Due to the time required to keep everything up to date, these small updates often fall by the wayside and over the months and years your projects can get painfully out of date. By employing a set of bots to do the heavy lifting for you, it's much easier to keep everything up to date.
That's all for now! Happy coding!
Check out the other articles in this series! This was part 6.
Interested in going deeper, and getting things like Preview Environments on every Pull Request? In my new masterclass, Cloud Native Entrepreneur, you'll do just that by engineering end-to-end marketing systems with microservice backends and running them in production with Kubernetes! All while learning marketing and entrepreneurship along the way! Check my Hackernoon profile to find how to register for a free info session!
Best,
Patrick Lee Scott

Written by patrickleet | HackerNoon's first contributing tech writer of the year.
Published by HackerNoon on 2020/01/23