エクサウィザーズ Engineer Blog

株式会社エクサウィザーズのエンジニアチームブログ

Continuous delivery of iOS using GitHub Actions + Fastlane, complete on GitHub

f:id:tadashi-nemoto0713:20210224120254p:plain

The Japanese version of this blog post can be found here:

techblog.exawizards.com


Hello, I'm Tadashi Nemoto from the Platform Engineering team(previously DevOps team).

In the last article, I introduced how to improve an API / Frontend deployment flow using GitHub Actions + GitLab Flow.

techblog.exawizards.com

I also improved the continuous delivery process of an iOS app by using GitHub Actions, and I would like to introduce it in this article.

I believe this continuous delivery can be applied not only to iOS, but also to Android and other multi-platform frameworks such as Flutter.

Adoption of Git Flow・Brief explanation of Git Flow

In the last article, I mentioned that we adopted GitLab Flow for our API / Frontend deployment flow and branching strategy.

However, I thought that GitLab Flow or GitHub Flow would be incompatible with our mobile release process because of the following factors:

  • We need to update the version number for each release.
  • A review from Apple / Google is needed for each release, so we cannot release all the time

On the other hand, some companies use trunk-based development as the release flow for mobile apps.

However, trunk-based development more suitable for relatively large-scale app development and deployment schedules, such as releasing once a week, and I thought that it did not match the current state of our mobile development.

For the above reasons, I chose Git Flow, for our mobile app deployment flow branching strategy.

f:id:tadashi-nemoto0713:20210118184341p:plain


To make the rest of this explanation easier to understand, let's take a brief look at Git Flow.

f:id:tadashi-nemoto0713:20210118184235p:plain

① Create a feature branch from the develop branch when you add a new feature. When its task and review are done, merge this to the develop branch.

f:id:tadashi-nemoto0713:20210316161849p:plain


② Create a release branch from the develop branch when it's ready for preparing the release.

f:id:tadashi-nemoto0713:20210212164923p:plain

③ After some checks and modifications on the release branch, merge this to the develop・master branches.

f:id:tadashi-nemoto0713:20210212182725p:plain

④ After merging the release branch to the master branch, add a tag and release to production (For mobile development, submit the app to Apple Store Connect)

f:id:tadashi-nemoto0713:20210212182646p:plain

⑤ If there are fatal bugs after release, hotfix release. First, create a hotfix release branch from the master branch.

f:id:tadashi-nemoto0713:20210212171006p:plain

⑥ When you have finished fixing and checking on the release branch for the hotfix and are ready to release, merge it into the master・develop branch and release it again to the production environment.

f:id:tadashi-nemoto0713:20210317122731p:plain

In the next section, I'll explain how we achieved continuous delivery within this Git Flow.

Continuous Delivery of iOS using GitHub Actions + Fastlane

Create release branch and Pull Requests

When the development on the develop branch described in ① has progressed and you are ready to release, you can create the release branch described in ②.

f:id:tadashi-nemoto0713:20210212164923p:plain
Create release branch

Release branches are often created manually, but in this case, I was able to automate the process by using GitHub Actions + Fastlane.

First of all, we want to use Semantic Versioning to specify the release version.

With Semantic Versioning, we can regularize the versioning of releases by a Major, Minor, and Patch version increase. (I won’t explain Semantic Versioning in-depth here, as it's already explained well on other websites).

f:id:tadashi-nemoto0713:20210305142013j:plain:w200
Semantic Versioning

Fastlane provides an Action called increment_version_number and it increments the version number bypassing parameters(Major, Minor, Patch).

And below Fastlane will do the followings:

  • Increment the version number based on Semantic Versioning and commit files.
  • Create a release branch and push to GitHub
  • Create a Pull Request to the master and develop branches

f:id:tadashi-nemoto0713:20210315131519p:plain

Currently, increment_version_number only supports iOS, the below article describes how to increment version number automatically for Android.

Automating semantic versioning model in mobile releases | ThoughtWorks

Finally, we'll make this Fastlane run via GitHub Actions

f:id:tadashi-nemoto0713:20210312182827p:plain

There are several types of workflow triggers in GitHub Actions, but in this case, we will use workflow_dispatch, which can be triggered manually from the GitHub Action UI, since we want to be able to run at any time.

Events that trigger workflows - GitHub Docs

Also since workflow_dispatch also accepts arguments, we can pass Major or Minor for this upgrade (Patch is only used for hotfix releases, so we won't use it here).

f:id:tadashi-nemoto0713:20210314171333p:plain
workflow_dispatch on GitHub Actions

By doing this, I was able to automate the process of updating the release version and creating release branches and pull requests by triggering the workflow by specifying Major or Minor in the GitHub Actions UI.

f:id:tadashi-nemoto0713:20210312183859p:plain
Pull Requests to master・develop branches

f:id:tadashi-nemoto0713:20210302224859p:plain
File diff for version updates

Merging two Pull Requests at the same time

In the previous step, two pull requests were automatically created from the release branch to the master・develop branches.

Then, as explained in section ③, when the release branch is ready to be released, you can merge it into the master・develop branches.

f:id:tadashi-nemoto0713:20210212182725p:plain

Of course, you can merge the two pull requests manually, but there is a possibility that you might forget to merge one of them.

To merge the two pull requests at the same time without forgetting, I created the following GitHub Actions workflow.

f:id:tadashi-nemoto0713:20210312181035p:plain

This workflow allows you to merge two pull requests for the develop and master branches at the same time if you add the label release to the pull request.

f:id:tadashi-nemoto0713:20210314132857p:plain

Creating a Tag & GitHub Release, submit App Store Connect

When the release branch is merged into the develop・master branches and a new commit is made in the master branch, you can create a tag and release it to the production environment as described in ⑤ in Git Flow. In the case of iOS development, this is often the time to submit to App Store Connect.

f:id:tadashi-nemoto0713:20210212182646p:plain

Creating a Tag & GitHub Release triggered by a commit to the master branch can be automated with the following GitHub Actions.

f:id:tadashi-nemoto0713:20210314142612p:plain

f:id:tadashi-nemoto0713:20210314172613p:plain
Creating Tag & GitHub Release

We will also build and upload the release version of the app to App Store Connect at this time.

f:id:tadashi-nemoto0713:20210314172439p:plain

Here we are utilizing Fastlane's Deliver Action.

The Deliver Action allows you to not only upload to Apple Store Connect, but also upload metadata screenshots, submit, and automatically release after approval.

Hotfix release

The above is the normal release flow.

In addition to the regular release flow, we will conduct a hotfix release if a serious bug is found after the release.

Follow the steps ⑤ and ⑥ to create a release branch for the hotfix from the master branch, and then merge it into the master/develop branch for release.

f:id:tadashi-nemoto0713:20210212171006p:plain

When we do a hotfix release, we follow Semantic Versioning and update only the Patch part (x.x.0 → x.x.1).

f:id:tadashi-nemoto0713:20210305135019p:plain

Then, in GitHub Actions, checkouts from the master branch create a release branch and Pull requests.

f:id:tadashi-nemoto0713:20210314145833p:plain

Then, after the fix and confirmation on the hotfix release branch is done, you can do the same thing as a normal release

  • Add the label release to the Pull Request and merge it into the master・develop branch.
  • After the commit is made on the master branch, a Tag & GitHub Release is automatically created, and the release version app is submitted to App Store Connect.

【Optional】Distribute debug app (Firebase App Distribution)

Although not directly related to Git Flow, I will also briefly explain how to use GitHub Actions to distribute debug apps.

To check the behavior of the application before release, we often use the following services to distribute and check the debug version of the application.

You can automate the building and uploading of the debug version of your app using GitHub Actions.

With Git Flow, you can trigger it when you finish feature development on the feature branch and merge it into the develop branch, or when you commit to the release branch.

f:id:tadashi-nemoto0713:20210212182754p:plain

The workflow in GitHub Actions is as follows.

f:id:tadashi-nemoto0713:20210305134944p:plain

In the above workflow, we have a workflow_dispatch as a trigger, in addition to committing to a specific branch.

When you are developing a feature in ①, you may often find yourself in a situation where you don't want to merge it into the develop/release branch yet, but you want to build and distribute a specific feature branch.

f:id:tadashi-nemoto0713:20210317125311p:plain

You can specify the branch and trigger workflow in GitHub Actions page, using workflow_dispatch.

f:id:tadashi-nemoto0713:20210212184657p:plain
workflow_dispatch

Summary

Finally, I'll summarize the steps I've taken so far in using GitHub Actions for iOS continuous delivery.

【Normal release flow】

  1. Create a feature branch, merge it into the develop branch

  2. When you are ready to prepare the next release, go to the GitHub Actions page and trigger the workflow for preparing the next release. You should choose the bump type parameter(major or minor). f:id:tadashi-nemoto0713:20210314171333p:plain

  3. GitHub Actions checks out the develop branch, bumps the version based on the parameter, creates a release branch, and creates Pull Requests to develop・master branches. f:id:tadashi-nemoto0713:20210312183859p:plain

  4. At this time, the debug version of the app will be built and distributed to Firebase App Distribution, so you can check its behavior with actual devices.

  5. When it is time to release, Add the label release to Pull Request and merge it into the develop・master branches at the same time. f:id:tadashi-nemoto0713:20210314132857p:plain

  6. Commits are made to the master branch, automatic Tag & GitHub Release creation, and submission to App Store Connect. f:id:tadashi-nemoto0713:20210314172613p:plain


【Hotfix release flow】

  1. When you're ready to prepare a hotfix release, run the hotfix release preparation workflow from the GitHub Actions UI
  2. Update the release version (Patch), create a release branch, and create Pull Requests to the master・develop branch.
  3. Commit the bug fix to the above release branch.
  4. Each commit to the release branch automatically builds a debug version of the app and distributes it to Firebase App Distribution, so you can check how it works on an actual device.
  5. Once the fix is confirmed, Add the label release to Pull Request and merge it into the develop and master branches at the same time.
  6. Commits are made to the master branch, and a Tag and GitHub Release are automatically created and submitted to App Store Connect.

This has allowed us to complete most of the work required for the release on GitHub! (Depending on how much of the work to Apple Store Connect is automated by Fastlane's Deliver action.)

While releases in mobile app development tend to be less frequent than API and front-end development, there is a lot of work that needs to be done for a release.

With this continuous delivery process, I hope to become:

  • Able to release app improvements to the market with more agility
  • More focused on product development itself

hrmos.co

References