Automating dependency upgrades with GitHub Actions and Dependabot
Dependabot is a great tool which can be used to update dependencies with significantly less effort because far less human interaction is required. And now that it has become a GitHub-native app, integration into any project is fairly hassle-free. Even so, issues can arise due to a number of reasons, especially if Dependabot is set up to scan for updates often enough (which it probably should be). In a nutshell, there is still a good amount of manual work involved; compatibility testing should be performed, followed, of course, by the approval and merging of PRs. This called for an automated CI process to be put in place - which is what we'll go through in this article.
Dependabot Set Up
As mentioned earlier, Dependabot can be easily added to a project. All you need to get started are a few adjustments to the "Security & analysis" settings of your chosen repository.
Dependency graph must be switched on first; it is then recommended to enable all of the available alerts. This allows Dependabot to continuously scan for dependency upgrades.
Extended configuration can be turned on by adding a file under the .github/dependabot.yml
path in a given repository. It can be useful if you’d like to keep any dependencies at specific versions and therefore ignore any proposed updates that may be raised in PRs. For instance, we ignore serverless-related dependencies as we only use certain versions alongside the serverless starter kit, and upgrading those would be a far more complex process. The configuration also makes it possible to set up a schedule and an open PR limit so you wouldn't get overwhelmed by too many updates all at once.
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
When Dependabot PRs get raised, there is a set of actions users can manually perform on them by adding comments. Some notable ones are:
-
@dependabot rebase
pulls in the latest version of the code from your main branch -
@dependabot recreate
creates a new version of the PR which can be useful if there’s a lot of history on it -
@dependabot merge
merges the PR once the status checks have all passed (can be done manually as well) -
@dependabot ignore this major version
-
@dependabot ignore this minor version
-
@dependabot ignore this dependency
- these can be used to tell Dependabot to ignore certain versions or dependencies and stop raising PRs for them. Nevertheless, if necessary, it's much better to set this up in the config file within the repository as it's much more visible and easier to manage there.
Adding Dependabot to the CI flow
Our main goal is to have an automated process allowing to keep dependencies up to date - making it so there would be less manual work involved. Several approaches were tested in order to enable this flow.
Current flow
A few GitHub Actions workflows have already been defined within our project. The first one runs unit tests and linting on every PR. The second one is for deployment to stage and only gets triggered when code gets pushed to master. We need the Dependabot job to be an intermediary step between these two.
Dependabot Auto-Merge action
One option is a GitHub action for auto-merging Dependabot PRs called Dependabot Auto-Merge.
The workflow itself requires a PAT (personal access token) to be generated and added as a repository secret. It’s usually possible to use a GitHub token which gets created at the start of a workflow run; however, in the case of this action, it does not have the required permissions to merge a PR.
Although the action functioned well within the main Dependabot workflow, it caused us issues further down the line. Once the dependency job was merged in, the workflow which deploys to stage stopped functioning properly.
When investigating this, the first thing that stood out was: Dependabot being the PR owner and therefore the active user that triggered the push to master. Turns out that after some recent security updates to Dependabot itself, that user cannot access secrets stored within a repository. Thus, an action which required secrets now failed to work - making the deployment not possible anymore.
As noted in this thread, there are multiple ways to deal with that. Regardless, after a further look into it, we found that the auto-merge action works best if a deployment can be triggered elsewhere, that is, not through GitHub Actions.
Custom auto-merge action
A different approach was needed to implement the auto-merge. Essentially, it's a sequence of steps based on Dependabot/GitHub Actions automation examples. This works quite well within the full CI flow as Dependabot waits for all Status checks (ie linting/unit tests) to pass before triggering the workflow at hand.
In this scenario, the default GitHub token can be used. It just needs to have its permissions defined correctly to obtain the right scope for git commands. These are usually placed at the top of the workflow file just before your list of jobs:
permissions:
pull-requests: write
contents: write
The job steps on a PR raised by Dependabot are then set up as follows:
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v1.1.1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- Dependabot metadata: Dependabot action to Fetch PR metadata
- Approve a PR: automatically approve the PR as a
github-actions
user - Enable auto-merge for Dependabot PRs: merge commit the PR (as per our repository merge settings)
Next steps for deploying to staging also have to be a part of this workflow. It helps isolate dependency updates from regular code updates but there is slight duplication between the two actions.
Additionally, a concurrency flag was introduced to both Dependabot and deployment flows. It essentially groups deployment jobs so they could only be triggered one at a time. This was due to several dependency updates getting merged in all at once, which set off too many deployments and broke our CI process.
concurrency: staging_environment
One downside to having multiple GitHub Actions workflows is that they are still visible on regular PRs, so while the code is much tidier and we can manage it better, it can look slightly messy within the UI.
To have further visibility for our CI flow, we've also implemented a Slack notification in case any of the steps above do fail. This was set up via a Slack hook and a custom GitHub action that posts a message to the dev channel.
Next Steps
The integration described in this article should be able to get you started but generally, there are plenty of ways to max out Dependabot’s capabilities.
For instance, maybe not all the PRs should be automatically merged, so it could be worth configuring some labels to filter out and prioritize dependency updates - especially on a larger project. There are certain actions that can be used to automate labelling as well. Dependabot could also run updates on package.lock files as these can sometimes become inconsistent. All in all, the automation can be extended a whole lot depending on what your requirements are.