I think I’ve got this mostly figured out now! The crux of the issue is this:
Any Buildkite pipeline that you want to have gate your merge queue needs to be in the required status checks for the ruleset you’re using to protect your main
branch. And any check that’s a required status check needs to pass on a pull request to be able to get the PR in to the merge queue.
So here are the two scenarios I’ve tested and my testing results.
Can you check them against what you know and use on your side @benmc?
Scenario 1: You want to run the same pipeline against PRs and merge queue branches
If you’re running the same Buildkite pipeline on PR open and on entry in to the merge queue the setup is pretty straightforward.
You do not want to put a branch limiting pattern on your Buildkite pipeline in this case. You just want to make sure that the following options are checked for the pipeline:
- Skip build with existing commits
- Build pull requests
- Skip pull request builds for existing commits
- Build branches (this one is necessary for the merge queue)
- Update commit statuses
You can use whatever you want for the rest of the options, but those are the essential ones to have turned on.
On the Github side you’ll want to make sure that the buildkite/my-pipeline
pipeline is listed as a required status check in either your ruleset or your branch protection rules where you’re enabling the merge queue.
You should make sure your pipeline code works in both the scenario where you’re running against a pull request and running against a branch update. The big difference between the two cases is branch runs will have BUILDKITE_PULL_REQUEST_BASE_BRANCH
set to an empty string on branch builds – so if you’re relying on that to get a list of changed files or whatnot, you’ll need to anticipate it being ""
and adjust your pipeline code accordingly.
Scenario 2: You want to run different pipelines against PRs and merge queue branches
This is the harder case.
Both of these pipelines need to be in the branch protection rules on Github as required status checks. That means they’ll both run during pull requests and during merge queue branch creation.
In our case, we use buildkite-builder to build our pipelines. So the first pipeline step is always “generate the actual pipeline”. This makes it easy to check the branch name and, if it doesn’t match a pattern (or does match a pattern), short circuit the pipeline.
In the pipeline we only want to apply to pull requests, we have the Ruby code check to see if the branch name starts with gh-readonly-queue
– if it does we don’t generate any steps and the pipeline stops there and is green. If it doesn’t, we generate all the steps we’d like to run during a pull request.
In the pipeline we only want to apply to merge queue branches, we do the same check for the branch name, but if we get a match we generate steps. Otherwise we stop there.
This ensures both pipelines run on PR and branch creation. But they do work in the scenario we want them to do work.
You could, of course, use the same check to make a single pipeline do different things in the PR and merge queue branch case, but we have long and complex pipeline files and liked the physical file separation we got doing it this way. It also made the UI nicer as engineers seeing a failure in the merge queue see it as “the merge queue pipeline failed” not “the PR pipeline failed” so it’s easier to know what to debug and where to look.