At Swvl, our release process started to get a bit tedious as we started using pull requests to make new releases for the iOS App Store. We were starting to feel the fatigue of manually completing the exact same steps for every release, and we started to experience problems due to the process involving many manual steps, which are prone to human error.
Our goal was to make this process as seamless as possible for the engineer responsible for the release process. We decided to use GitHub Actions to automate as much of the process as possible. In this article, I’ll explain different parts of our process and show you how we were able to set up GitHub Actions to achieve what we needed.
Before we start…
GitFlow
We use GitFlow for our release process, which means when we are ready to release, we create a release/{version_number}
branch from develop
. We would manually create this branch, change the version number, generate Changelogs, push the changes and create two PRs for this, one for the merge into master
, and one for the merge back into develop
. Once a release was merged, we needed to do some release note creation, tagging on Github, and notifying the team on Slack what the latest release contains.
GitFlow is a branching model for Git that uses branches liberally. If you’re unfamiliar with GitFlow; here is the gist:
- Your main branch is called
develop
ordev
. This is where the work happens. - All feature additions branch off
develop
and get merged back into it after the change is complete. These branches are usually called ‘feature’ branches. - To start a release, a new branch from
develop
is created with the name formatrelease/{version_number}
. - Once a release is ready, the branch is merged into
master
. The resulting merge commit is tagged. master
is then merged back intodevelop
to updatedevelop
with the changes done on the release branch (bumping version number, changelogs, …etc.)
There is more to it than just this; for example, how to deal with hotfixes, but that is out of the scope of this article.
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, which allows us to build a structured automation workflow easily.
GitHub Actions
GitHub actions are a way of executing some workflow 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 can also trigger these actions/workflows manually, which will come in handy in our case; we are going to use a combination of manual triggers and 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
develop
andfeature/*
branches. - Build and test release branches (those starting with
release/*
)
A Fully Automated Release Workflow
In GitFlow, making a new release typically involves the following steps:
- We create a new release branch, update the changelogs, bump the version in project files, make a commit and push it.
- We open a pull request against the
master
branch (remember that we branched offdevelop
). - Our CI system builds and tests the app.
- We merge the pull request if everything still works.
- We rebase (or merge) the
develop
ontomaster
. - We tag the merge commit on the master branch with the new version.
- Our CI system publishes the new version to the app store.
Here is how we are going to automate this:
To trigger the automated workflow, we are going to trigger it manually from GitHub’s actions web interface with the desired build number, then the GitHub action should:
- Check out the current
HEAD
ofdevelop
. - Extract the version number from the workflow’s input.
- Create a new release branch:
release/0.5.0
- Update the changelogs.
- Make a commit to update the changelogs.²
- Bump the version number in the project file.
- Make a commit for bumping the version number.
- Push the commits to the origin.
- Open a pull request and request reviews from the Github user who triggered the Github action.
- Opening the pull request will trigger our CI system and build and test the software. If everything works out, we will approve and merge the release branch.
We also created a second GitHub action 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:
- Check out the current
master
branch. - Create a new release on GitHub (this also creates a tag).
- Finally, the same GitHub workflow will create a pull request for merging the
master
branch back intodevelop
.
The Tricky Parts
Since the ‘start a new release’ workflow would be triggered manually, we should use workflow_dispatch
with a single input; the version number.
After creating the release branch and the necessary configurations, another tricky part is bumping the version number inside the project.pbxproj
file. We are using sed to do so. The query itself is a little bit complicated, but it gets the job done at a blazing-fast speed.
After generating the changelogs, committing the changes, and pushing the branch, we use an already available step to create the pull request in our desired format and style.
Then for the other workflow, which should be triggered after this pull request is merged, we use this condition to only trigger it from release-related pull requests.
We then extract the version number from the branch name and save it in a variable to be used later to create a release on GitHub and open another pull request to sync the changes back to development.
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 permanently or might just not be available).
Automating as much as possible empowers your team members, and in our case, all they need to do is trigger a Github action with the version number and approve the automatically created pull request. That’s it!
¹ It is important to note that GitHub actions do 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.
² 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.
Author
Mohamed Tarek