I have a lot of NPM packages. Like "more than a 400" a lot. Every package falls behind its dependencies as soon as it is published to the NPM registry. Periodically I get new GitHub issues like this one asking me to upgrade a particular dependency and publish a new version to get around a bug or a known security issue.
Manual dependency upgrades are soooo boring. Grab the code, install dependency, increment version for a dependency, run the tests... Boring! So I have written a command line tool next-update to automate this process. The tool helps a lot. But it is still a hustle to run the tool - remember, the number of out of date dependencies is constantly growing, while my time is pretty much constant.
next-update-travis
So I have automated running next-update
- by periodically running it as a Travis cron job. Here is the tool called next-update-travis in action - each Travis build marked "Cron" is the automated dependency update check. Successful updates are committed to master
triggering another "normal" Travis build marked with an orange arrow in the screenshot below.
While that works, this approach lacks any flexibility. I could only test dependency upgrade and merge the result - I could not review it. Yes, I could change the code to open a pull request, but then the tool would require a lot more to be useful. It would need logic to NOT open multiple branches for the same dependency, merge some dependency upgrades but not others, etc. So I needed a more flexible solution.
greenkeeper.io
My first impulse was to look at the service offered by my friends from semantic-release organization. After all - they have written the only tool I always use to publish my NPM packages! They must know how to organize the workflow that makes dependency updates painless. In fact they do have a great tool called greenkeeper.io to be installed as a GitHub application. Free for public repos, it monitors all versions of your dependencies and opens pull requests when a new version comes out.
The onboarding process is very intuitive, and the updates start flowing a few seconds after installing the application. The pull requests are very intelligent and are tied to the CI statuses. Just review the changelog for a dependency included in the pull request and merge it! The app even merges multiple dependency version updates into a single pull request and rebases it when the master changes.
But many, many pull requests opened for a couple of my repositories soon overwhelmed me. Here is a screenshot of a relatively small project - see all these pull requests from Greenkeeper?! It looks even worse for the closed pull requests - 9 out of 15 closed PRs are dependency updates.
Often, I am looking at a pull request from Greenkeeper.io to merge a "patch" dependency upgrade and ask myself "why can't Greenkeeper merge this pull request automatically?" If my code is well tested, if the update is just a "patch" from a trusted library, and if the CI tests are passing - that should be a no-brainer! Yet there is no such option. So I need to continue my search.
greenkeeper-keeper
I found greenkeeper-keeper by the great Matt Travi (Hi Matt!) that can automate merging of "green" pull requests from Greenkeeper. Yet I would need to run the greenkeeper-keeper
server myself ... and I already have too many services running and would prefer to avoid adding anything else to my plate. But I know Matt is going to make an awesome tool and service for dependency management some time in the future.
RenovateApp
Then I tried Renovate App from Rhys Arkins with whom I have a pleasure of talking from time to time about dependency management. Again, this application is a GitHub app, which means I can just select a few public repositories via GitHub configuration page - and it is up and running!
A good example project where you can see Renovate App running is my bahmutov/snap-shot-it.
RenovateApp has a huge number of configuration options. But to start I placed a simple JSON file into my repositories (or I can just wait for the first pull request from the renovate[bot]
to add this file to my repository). Here is the simplest configuration I tried
1 | { |
My project will use mostly the default settings (config:base
), but will allow Renovate to automerge passing pull requests after a short period of time. This short period of time is about 1 hour and is very convenient because I can look at the pull request and merge it myself quickly.
Each pull request from RenovateApp has the commit log between the current version of the dependency and the target one. For example see this pull request
The pull requests are quickly adding up, but there are configuration options that allow limiting their total number and frequency to avoid too much noise.
Notice that some pull requests are chore(...)
and some are fix(...)
. By default (this can be configured), production dependency upgrades get message fix(...)
triggering publishing of a new "fix" release of my NPM module (if I have configured semantic release). Updates to the devDependencies
are only getting a chore(...)
commit message, NOT triggering a new version release.
No need to worry about all these pull requests because ... most of them will be merged automatically!
Look at the renovate-bot
chugging along, moving code into the master
without any effort! Of course I must set up good testing for my repositories to avoid accidentally breaking things. The automerging is disabled for "major" dependency updates. Thus a relatively dangerous upgrade from Lodash 3.x.x to 4.x.x will require my review and approval, while the backwards-compatible minor and patch updates are merged by the Renovate bot. When semantic-release
publishes a new version it even writes a comment under the pull request to tell the new NPM version; isn't it great when tools play nicely together?
I have limited my initial exploration of Renovate App to just updating dependencies, but there are a lot of other things it could do. I hope to investigate the app further in the future. Renovate App looks like a winner.
Just to mark every repo that has Renovate installed I created a couple of badges that look like this
Below is the Markdown markup.
1 | [![renovate-app badge][renovate-badge]][renovate-app] |
Feel free to use these in your README files.
Final thoughts
- next-update is my "go to" tool for upgrading dependencies from command line
- next-update-travis is a great tool to keep dependencies up to date for small well tested projects
- greenkeeper.io is awesome if you plan to review every dependency update
- greenkeeper-keeper is a small add-on service to automerge passing dependency updates
- RenovateApp seems like a tool I will be using from now on. Easy to use, powerful configuration, automatic merging when configured - this tool can completely automate the grunt work, leaving me to pursue more intellectual activities, like binge watching TV shows.