Using GitHub actions and GitFlow to automate your release process

This blogpost describes an idea of using GitHub actions and GitFlow to automate away most of the steps that are involved in creating a new release. The end goal is to create a release of your software with only two steps:

  • Creating an issue
  • Merging a PR

Note that both of these can be done from within GitHub's web interface and without commandline interaction.
As a result, this strategy is very accessible and doesn't require deep understanding of the steps necessary to create a release.

Prerequisites

This workflow assumes the following:

  • You are using GitFlow as your branching model
  • Your code is hosted on GitHub and you have access to GitHub actions
  • You have a CI system that builds and tests pushed branches/commits

GitFlow

GitFlow is a branching model for Git that makes fairly liberal use of branches. For people unfamiliar with GitFlow, here is a quick primer:

  • Your main branch is called 'develop' or 'dev'. This is where the work happens.
  • All code-changes need to branch of 'develop' and merge back into it after code-review. These are called 'feature' branches.
  • To draft a release, we branch of a 'release/{version}' branch.
  • To finalize a release, the release branch is merged into 'master'. The resulting merge-commit is tagged with a tag.

There are more details to it in regards to hot fixes but for the our case, these don't matter.
What is interesting about GitFlow is that through this particular use of branches, we are very explicit about what we are currently doing and what is supposed to happen next.
This allows us to build automation around events that happen in our CI system.

GitHub actions

GitHub actions are a way of executing custom code as a result of events happening on GitHub.
They use the same event system as GitHub's webhooks, hence there is a whole bunch of them, for almost anything that happens on Github.

We are going to use the events triggered through our use of GitFlow to automate our release process with GitHub actions.

CI configuration

Your CI system should be configured to:

  • build and test your dev and feature/* branches
  • build and test release branches (those starting with release/)1

A fully automated release workflow

In GitFlow, making a new release typically involves the following steps:

  1. We create a new release branch, update the changelog, bump version in manifest files, make a commit and push it
  2. We open a pull request against the 'master' branch (remember that we branched of 'dev')
  3. Our CI system builds the software in release-setting
  4. We merge the pull request if everything still works
  5. We merge the release branch back into dev
  6. We tag the merge commit on the master branch with the new version
  7. We publish the new version to a package registry or deploy it somewhere if it is an application

Here is how we are going to automate this:

To trigger the automated workflow, we are going to open an issue on our repository with a title like 'Release version 0.5.0'. We create a GitHub workflow that listens on the IssueCreated event and:

  1. checks out the current HEAD of 'dev'
  2. extracts the version from the issue title
  3. creates a new release branch: release/0.5.0
  4. updates the changelog2
  5. bumps versions in the manifest files
  6. makes a commit
  7. pushes the commit to origin
  8. opens a pull request and requests reviews

Opening the pull request will trigger our CI system and build and test the software3. If everything works out, we approve and merge the release branch. We create a 2nd GitHub workflow that listens on 'closed' events for pull requests that target the 'master' branch. In GitFlow, this only happens for releases (and hotfixes). This GitHub workflow will:

  1. Checkout the current master branch
  2. Create a new release on GitHub (this also creates a tag)
  3. Build desired release artifacts and upload/deploys them
  4. Finally, the same GitHub workflow will create a pull request for merging the release branch back into dev.

You can see all of this in action here: github-action-gitflow-release-workflow

Hotfixes

In GitFlow, hotfixes are (usually urgent) changes to a version of the code that is currently running in production. This is achieved by checking out the tag that is associated with the affected version (f.e. 1.5.0) and branching of a new hotfix branch (hotfix/1.5.1).
On this branch, the hotfix is implemented, tested and eventually merged into master and dev.

In terms of automation, there is little difference between a hotfix and a release branch being merged into master. The example repository only makes a difference here in how the version is extracted from the branch name.

Conclusion

Releasing a new version is something everyone on the team should be able to do. You never know, which people will leave the team permanentely or might just be not available, yet an urgent bugfix needs to be released.

A branching model like GitFlow already helps to standardize, what needs to be done in such a situation. Automating as much as possible empowers your team members even more. In our case, all they need to do is create an issue with a specific title and approve the automatically created pull request. That's it!

FAQ

Why do we need to use GitFlow for this automation to work?

The impact of GitFlow is fairly subtle, yet important. What we need is a distinct event that triggers the actual release. In GitFlow, that is the merge of a release branch into master. Without the distinction between master and develop, releasing the merge-commit of a release branch into master might accidentially increase the scope of the release if any other pull request gets merged into master after the release branch was branched of.

With GitFlow, release branches are always branched of develop and merged into master. This guarantees that the scope of a release is always the diff between the last release and the HEAD of develop at the time the release branch is branched of.


  1. Depending on your software, you might want to run a 'release build' in this scenario. For example in Rust, cargo has a switch --release. Your CI should be configured to use such functionality when building a release/ branch 

  2. I am assuming here that we are using keep-a-changelog. With keep-a-changelog, the entries are written together with the code changes under an 'Unreleased' section. To create a new release, we only need to rename this section to reflect the new version. 

  3. It is important to note that GitHub actions does not trigger events for actions that have been performed by a GitHub workflow. A GitHub workflow that pushes a branch (like in our case) will not trigger the push event. Unfortunately, that means we cannot use GitHub actions to build your release/* branches and we will have to use a 3rd party CI system like Travis, or CircleCI. 


You'll only receive email when Thomas Eizinger publishes a new post

More from Thomas Eizinger