Handling multiple pull requests

Matej Lednicky
Typeform's Engineering Blog
4 min readApr 27, 2023

Everybody building a JavaScript project or library is familiar with dependencies. Everybody maintaining such a project is aware we need to keep those dependencies up to date. Not just for the sheer fun of it (spoiler alert: it is not 🫠) but to keep your app safe. Yes, those dependencies your app would not function without are built by developers just like you, and they make mistakes too. Of course, when that happens, open-source libraries get fixed (especially the popular ones), and a new version is released. And you need to install the fixed version in your app to make sure your app is not vulnerable (anymore 😅).

Or maybe you just want to get the latest features of a specific (internal) dependency.

So how do we keep up, you ask?

Is it a bird? Is it a plane? Is it superman? It’s … Dependabot!
Photo by Yogi Purnama on Unsplash enhanced with DALL·E 2

Dependabot to the rescue 🦾

You can use Dependabot (or a similar alternative such e.g. Renovate Bot). I am not saying one is better than the other. My personal preference is Dependabot simply because it is available in GitHub out-of-the-box, and I was also used to it already from previous projects. We are also using it at Typeform to keep internal dependencies up to date across multiple projects.

And quite often, the pull request list looks like this:

List of 6 pull requests on Github from Dependabot
All PRs and no play makes Matej a dull boy 🥴

What do we do now?

  1. Open the first PR from the top
  2. Rebase PR on the latest main branch
  3. Wait for all PR checks to pass ⏳
  4. Optional: Manually check the version deployed from the PR
  5. Approve and merge PR
  6. Wait for the canary release to pass ⏳⏳⏳
  7. Repeat for each pull request opened by the Dependabot 🤬

Sounds like fun? Did not think so…

This will also affect your continuous integration tool. If you are paying by the minute it will increase your bill (running PR checks multiple times, running the release job multiple times — which takes time and money if you are deploying via canary releases), or if you are paying per concurrent job it will block your resources and might delay other PRs and projects (and you might need to pay for more concurrent jobs).

So what do we do now?

💡 Note: Dependabot has released grouped version updates in public beta at the end of June. You might want to check it out.

Add some GitHub actions to the mix ⚙️

We can use a GitHub action to merge all commits from different pull requests by Dependabot into a single branch and open a new pull request.

How does it work?

  1. Find all PRs by Dependabot based on a “dependabot” branch prefix
    (Optional: Check if all PRs checks passed or ignore PRs with a given label, e.g, “nocombine”)
  2. Checkout a new branch from main branch
  3. Merge each branch from Dependabot PRs (skip in case of conflicts)
    Full disclosure: after using the action for some I found out it makes quite a lot of conflicts especially when the updated dependencies are on lines one after another, which is often the case for our dependencies prefixes with @typeform/
  4. Open a new PR with multiple dependency bumps

You can find the action in our public Typeform/.github repo:
https://github.com/Typeform/.github/blob/main/.github/workflows/combine-prs.yml

The action is inspired by hrvey/combine-prs-workflow which unfortunately is not a reusable action. It also improves the functionality by creating the new branch on the latest default branch (usually main) instead of one of the Dependabot branches.

Using the action is fairly simple. In your repo, create a new action, for example, .github/workflows/combine-prs.yml with the following content:

name: 'Combine PRs'

on: workflow_dispatch

jobs:
combine-prs:
uses: Typeform/.github/.github/workflows/combine-prs.yml@v1
with:
runsOn: '["ubuntu-latest"]'
secrets:
githubToken: ${{ secrets.PERSONAL_OR_ORGANIZATION_TOKEN }}

Since reusable workflows currently do not support setting runs-on in your action, you need to pass the value as input via with. It needs to be passed as a string containing an array to be parsable as JSON (using this workaround to set the runs-on option). You can run the action on GitHub-provided ubuntu-latest runner or use your own.

Please refer to the full template if you want to modify other inputs for your repo (branch prefix, validating PR checks, new branch name, or label to ignore PRs):
https://github.com/Typeform/.github/blob/main/workflow-templates/combine-prs.yml

What do we do now?

Run the GitHub action, wait for checks to pass, and merge a single PR 😎

Github action with “Run workflow” button to run it manually
You can run GitHub Actions manually

And then?

Let the action do its magic and kick back with a cold one 🍻

That’s all.

Do you like this GitHub Action? Want to use it? Let me know on Twitter @mathio28.

--

--