Dependency updates are an inevitable part of software engineering. With every major language offering its own package ecosystem, keeping up with these updates — some of which address critical security issues — can quickly become tedious.
TLDR?
The problem
I maintain several open source projects on GitHub. While GitHub provides a handy tool like Dependabot to regularly scan repositories and create pull requests for dependency updates, keeping track of these PRs can quickly become overwhelming. My notifications panel often looks like this:
In the screenshot above, only the last notification isn’t related to dependency updates.
Ideally, these PRs should be merged as soon as all checks pass, keeping my notifications page tidy and allowing me to focus on the PRs and issues that matter most.
After manually merging these dependency update PRs for a while, I realized I needed a better approach. I wanted automation that could merge these PRs if they met certain criteria, while still giving me visibility into what was merged. Additionally, I needed an easy way to monitor GitHub Actions runs across my repositories to quickly spot if any of these merges broke CI.
The solution
mrj
When I couldn’t find a ready-to-use solution for this problem, I decided to
create my own: mrj
.
mrj
is a simple command-line tool that takes a configuration describing
conditions, and then merges PRs automatically.
Its configuration looks like this:
# mrj.toml
# repos to run for
# (required)
repos = [
"owner/repo-1",
"owner/repo-2",
"owner/repo-3",
]
# mrj will only consider repos created by the authors in this list
# (required)
trusted_authors = ["dependabot[bot]", "github-actions[bot]"]
# by default mrj doesn't filter PRs by base branches
# (optional, default: empty)
base_branch = "main"
# by default mrj doesn't filter PRs by head refs
# read more on this here
# https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests--parameters
# the value needs to be valid regex
# (optional, default: empty)
head_pattern = "(dependabot|update)"
# by default mrj only considers PRs which can be cleanly merged
# if this setting is ON, mrj will also consider PRs where merging is blocked
# (optional, default: false)
merge_if_blocked = true
# by default mrj only considers PRs where checks have either passed or are skipped
# if this setting is OFF, mrj will not merge PRs where one or more checks have been skipped
# (optional, default: true)
merge_if_checks_skipped = true
# how to merge the pull request
# can be one of: [squash, merge, rebase]
# make sure the choice is actually enabled in your settings
# (required)
merge_type = "squash"
# what to sort pull requests by
# can be one of: created, updated, popularity, long-running
# "popularity" will sort by the number of comments
# "long-running" will sort by date created and will limit the results to pull
# requests that have been open for more than a month and have had activity within
# the past month
#
# (optional; default: created)
sort_by = "created"
# the direction of the sort
# can be one of: [asc, desc]
# (optional; default: asc)
sort_direction = "asc"
When run, it produces output like this:
As mentioned earlier, I also wanted visibility into which PRs were merged. To
address this, I added a feature to mrj
that generates reports based on past
runs. These reports can be hosted as webpages anywhere. Mine are hosted
here.
mrj
now handles merging all of my dependency update PRs. I run it on a
schedule here.
With automatic merging of PRs taken care of, I needed an easy way to spot if any
of these merges broke CI for my repositories. For this, I use a tool I created a
while back: act3
.
act3
act3
is a command-line tool that collects the results of the last three runs
of your GitHub Actions. It can take either a predefined list of workflows or a
list of repositories to fetch results for.
cat repos.txt
# dhth/act3
# dhth/bmm
# dhth/chronotes
# dhth/commits
# dhth/cueitup
# ...
# generate config with predefined workflows, and get result for them
act3 config gen \
-r "$(cat repos.txt | xargs | tr ' ' ',')" \
-n '^main$' \
>main.yml
act3 -g -c main.yml
# get results for a list of repositories
act3 -r "$(cat repos.txt | xargs | tr ' ' ',')" -n '^main$'
act3
can also generate HTML reports, which can be hosted anywhere. I run
act3
on a daily schedule (after mrj
has run), and have the reports deployed
to GitHub Pages (you can view them here). I generate reports for three
types of GitHub Actions workflows:
- merges to
main
- pull requests
- vulnerability checks (run on a schedule)
If I see a red cross anywhere, it’s a clear signal that a particular CI run failed, so I can address it quickly.
Conclusion
I’ve only recently rolled out these workflows, but they’ve already proven quite
useful. I let mrj
run every night to handle merging dependency update PRs for
me. Every now and then, I check the act3
dashboard — if I see all green check
marks, that’s a good sign that my repositories are in good shape.
Of course, there’s always a chance that a dependency update, even if it passes checks, could break some functionality in a project. To reduce the risk of this happening, I’ve been adding more and more tests — especially end-to-end tests — to my projects.
If you’re interested in automating your own dependency updates, check out
mrj
and act3
. Feel free to send suggestions or bug reports via
GitHub issues.