GitHub has recently improved its automatic pull request handling and branch protection features. I have found these features handy, and nowadays, I enable them regardless of the project type to help me automate the project workflows and protect the code from accidental changes and regression bugs.
The features I find most useful are
Until now, I have used GitHub’s branch protection feature to enable these additional shields. With this settings page, you can easily protect one or multiple branches by configuring the abovementioned options.
However, when enforcing the branch protection, it applies to all users. That also includes the bot user I am using in many projects to release: creating a release tag, updating version numbers, and committing these changes to the main branch.
Suddenly, when the branch protection is enabled,
the release bot cannot do its duties as it cannot push to the protected branch.
Error states: Protected branch update failed for refs/heads/<branch_name>, X of X required status checks are expected.
Therefore, to overcome this problem, I have configured the bot to use pull requests. These workaround pull requests have slowed the process and made it unreliable. In some cases, I have been using a user token with administrative permissions to make the releases, which I want to avoid as it has evident problems in the security model.
Finally, this week, I reserved some time to investigate whether it is possible to avoid these limitations. I had two targets: first, I wanted to protect the main branch from accidental pushes so developers could make changes only via pull requests vetted by the CI checks. Second, I wanted the release bot to be able to bypass these rules and push the tags and version changes to the main branch without issues.
I googled for an answer for a fair amount of time. It soon became apparent that many others were struggling with the same problem, but also that GitHub had released a new feature called rulesets, intended to solve the problem. However, a few examples were available, and the configuration of the rulesets was not intuitive. Therefore, I have documented the steps below if you wish to use a similar approach in your project.
The instructions below are three-phased:
The first step is to create a GitHub application that handles the git operations in the CI release process for you.
There are multiple reasons why I chose to make a dedicated GitHub application instead of using a personal access token or built-in GitHub Actions token directly:
One can use GitHub Applications for multiple and more powerful purposes, but the releaser bot only needs minimal configuration as its only duty is to do the releasing-related chores.
Start the application registration via user profile Developer settings
or
this link.
When creating the new application for the releasing functionality, the following settings need to be defined:
releaser-bot
Webhook/Active
, as we don’t need webhook notifications.Permissions
/Repository
/Contents
/Read and write
.Where can this GitHub App be installed?
Note: If you want to use the application in an organization’s repositories, make it public.Create GitHub App
.After creating the app, you will receive a note saying,
Registration successful. You must generate a private key to install your GitHub App.
Navigate to the private keys section and push the Generate a private key
button.
The private key file will download to your computer. Store it in a secure place; you will need it later.
Before using the application in your repository’s workflow:
Install App
section.Install
button.The rulesets feature will work on behalf of the branch protection settings. To avoid having overlapping configurations, remove first any existing branch protections.
The next step is to create the rulesets.
I crafted the following approach according to the original idea presented in the GitHub blog. The goal is to protect the main branch so that:
test
.You may modify the settings according to your needs. For instance, you may require additional status checks or require a review of the PR before one can merge it into the main branch.
First, we will create a rule for all users. We do not allow anyone to delete refs or force push changes.
Go to the repository settings and select Rulesets
:
New ruleset
by tapping the New branch ruleset
.Main: all
name for the ruleset.Enforcement status
as Active
.Bypass list
empty.Include default branch
(assuming the repository default branch is main).Rules
section, tick Restrict deletions
and Block force pushes
.Create
button.Then, we will create another ruleset that requires PRs and status checks for any user other than the releaser bot.
New ruleset
by tapping the New branch ruleset
.Main: require PR except for releaser
name for the ruleset.Enforcement
status as Active
.Bypass list
.Include default branch
(assuming the repository default branch is main).Require a pull request before merging.
Require status checks to pass
and Require branches to be up to date before merging.
Add test
as a required status check.Create
button.The final step is configuring the release process to use our newly created GitHub application.
To create a token for the releaser bot in the GitHub Actions workflow, we must have two secret variables available.
Go to repository Settings
/ Secrets and variables
/ Actions
. Create two new secrets:
RELEASER_APP_ID
: Copy and paste your GitHub application ID (app ID) from
the GitHub application settings page.RELEASER_APP_KEY
: Copy and paste the contents of the private key file downloaded earlier.In the release workflow, generate a token with the GitHub Application.
For this, you can use a special action, actions/create-github-app-token
,
that utilizes the secrets defined in the previous step.
You can use the generated token for the repository cloning step. One can access the token using the outputs of the token generation step. Since the repository gets cloned with the bot token, the bot user will perform subsequent git actions.
You can find the sample workflow in GitHub.
So, the release script can now push directly to the main branch as we use the releaser bot token and have configured a bypass rule for the bot user in the rulesets. At the same time, other users cannot push to the main branch but must create a PR for their changes.
That’s it! The above steps show how to automate project release workflow in GitHub Actions with a dedicated bot user while still having repository branch protections to shield from accidental changes and unvetted code. I hope the instructions are of use to you. I’m always happy to have comments and ideas for improvement; you can contact me on LinkedIn!