First look: Using GitHub actions for building iOS and Android apps
I am very excited about the recent announcement of Github Actions supporting CI/CD and I plan to migrate all my continuous integration to Github Actions. A few days ago I got access to the new Github Actions and tried to move over a React Native app from CircleCI to Github Actions. This is how it went!
Picking an operating environment
Github now supports running your tasks on macOS, which is the only option for building iOS apps. For Android, you have two viable options: Using a Docker base image (using the container
option) or running the process on raw Ubuntu. After having some permission trouble with one specific image, I decided to go with the Ubuntu 18.04 environment.
Using Actions or run raw commands?
Workflows are divided as follows: Workflows -> Jobs -> Steps. Each step allows you to either specify a command (for example- run: pod install
) or to use an ‘Action’ that somebody else has prepared and will run in a Docker container. I tried out actions for yarn
, fastlane
and for bundle
, but personally for this use case I prefer most of the time to just enter a bash command.
Installing Ruby, Node, Fastlane etc.
On this rather hard to find page you can see which software comes preinstalled on the Ubuntu and macOS machines. The default version of Node on macOS is rather old (v6.17.0) and the ruby
command was not available on Ubuntu. So I installed node
, ruby
, gem
, bundle
over the internet first.
Using secrets and environment variables
Unlike in CircleCI, you specify the environment variables you want to use for each step, not for the whole workflow. I specify some files that I don’t want to check into the repo as base64 encoded strings in the “Secrets” section of the settings of a Github repo and specify them using the env
section of a step. (Attention: In Linux, the parameter for decoding a base64 value is -d
, on macOS the parameter is -D
)
Sudo or not sudo
I feel that the permissions of the filesystem are more strict than they are on your local environment or on other CIs. I had to use sudo
for various commands on Ubuntu such as gem install bundler
or npx jetify
or even for fastlane android beta
.
Setting up the iOS keystore
When using Fastlane, there is a setup_ci
command that will create a temporary keychain on macOS. This is required, otherwise the build will get stuck while archiving. However, Fastlane does not yet recognize Github Actions yet as a CI provider and does skip this step. Therefore, make sure to set force: true
!
Syncing certificates and provisioning profiles
I have previously used Fastlane Match to auto-sync my iOS certificates with a private Github repo. For this, I could generate a deploy key on Github and add it to CircleCI, which then allows to clone a private repository on CircleCI. Ironically, it is not so easy to clone a private Github repo on Github Actions because you cannot add those SSH keys in the settings. Therefore, I have migrated to the other storage option that is supported by Fastlane Match: Google Cloud Storage Buckets.
Increasing the number of watchers
When bundling my JavaScript using Metro, the build would fail with error code ENOSPC: no space left on device
. This is a misleading error — what you have to do is to increase the number of file watchers on the machine (using this command echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
). Would be cool if this limit was higher by default, as this was no problem on CircleCI.
Watching the logs
Watching the logs scroll up the window is a very satisfying feeling. Recently, Github Actions started supporting streaming the logs, but there is one big caveat. You can only see the logs that were printed after you loaded the page.
As I mentioned before, my builds got stuck during the signing process, but since I did not leave the Github Actions page open, I do see no logs at all — the only option I have is to cancel the build (which can be delayed by 1–2 minutes) or to leave it running.
This is something that is really frustrating, and something Github has to improve!
When to trigger workflows
I don’t want to trigger an iOS and Android app build on every commit — that would take lots of build minutes and I don’t want to release every commit. I only want to trigger a build when I feel my app is ready to be distributed to the beta channels.
There are ways to have scheduled/nightly builds, or to react to events like issue comments, tags, releases, wiki updates etc, but there is no button to “Build now”.
The easiest way to trigger a build manually, I find, is to trigger a build when someone stars your repository. That way you can simply unstar and star the repository and the build starts running.
Pricing and Usage
There seems to be no way yet to see how many build minutes you have used and how much it’s going to cost (it might be that during the beta everything is free).
The pricing of GitHub actions is much better than that of CircleCI.
On the Linux/Docker front, you get 2000 minutes for free (3000 minutes with a Pro subscription). CircleCI has not only recently reduced their free tier from 1500 to 1000 minutes per month — it is now also split up into a weekly quota of 250 minutes per week which will go really fast.
Building on macOS costs 8 cent per minute, which matches CircleCI which offers 500 minutes for $39 + 8 cent per additional minute. But on Github, you pay per minute, which means it’s a lot cheaper if you stay under 500 minutes. Also, as far as I understand, you can use your 3000 free minutes also on macOS, which would be great as it would probably save me over $400 per year.
CircleCI offers 1x parallelization for Linux and 2x parallelization for macOS on the plans that I have so it means a lot of waiting, but on Github you can have 20x parallelization per repository.
Overall impression
Github Actions Beta is still very much imperfect and I experienced plenty of bugs and inconveniences in the UI, the docs and the platform in general.
CircleCI still has the better product overall, but the better pricing and integration will make me switch everything to Github Actions.
Github has in the past year improved so many small things in their product and how much they have improved Github Actions in just 8 months is unbelievable.
I go all on Github Actions because I believe their ability to iterate super fast and listen to features requests will make it better than anything else sooner than later.