We love automation at Plum, it’s been at the core of the company since the beginning.
Since we started working on the iOS app, in early 2019, we’ve employed various processes to automate and streamline the development and release cycle of our mobile apps — yes, I did say apps, hint *Android* hint. Join the beta!
Time and accuracy.
Developers don’t waste time continuously cutting new builds and on the flip side it ensures the most up to date builds are always available. This is very important for the feedback cycle between developers and designers.
Automation also guarantees accuracy, ensuring the tests are always ran and correctly actioned upon, build versions are correctly bumped and all checkboxes are checked.
At the core of everything sits Bitrise. Bitrise is a super customisable CI service. If you’ve ever used Zapier or IFTTT it’s like that but for continuous integration.
Bitrise allows you to construct workflows. A workflow can be set to run when a certain branch is pushed to.
We run two type of workflows: internal and public. The result of each workflow is an internal or public build of the app — hence the names 🙃
Pushing to master will trigger the public workflow and pushing to any other branch will trigger the internal workflow.
Our Git workflow on the iOS project is very simple. It’s a simplified Gitflow. The develop branch is our main branch. We use feature branches that get merged into develop when the feature is complete and ready to be tested and used internally. Then when we want to cut a new public release we merge develop into master.
Both workflows are exactly the same until the end, where the public workflow diverges.
Before any of the cool stuff can happen the CI server needs to set somethings up. This starts with cloning and checking out the branch that was pushed to, building any external libraries required (we use Carthage), bumping the build number and also specifying the current branch’s name in the apps Info.plist (more about this later).
Next the tests get run. If anything fails the build gets cancelled and reported.
Now we know everything is healthy, both workflows will create an internal build.
The Xcode project has two Schemes which define whether it is an internal or public build. Internal builds have access to a few internal tools that we use for debugging (more on this in another post!)
Now the build is complete we upload any dsym files to Sentry (our crash reporter of choice).
Next, the CI server updates our internal build dashboard. In the past I have just used a Slack channel to post build notifications in, however when there are multiple feature branches and multiple product teams wanting to get the latest build for their feature I found that there was a lot of confusion around which build should be installed. So I built a simple dashboard that displays the latest build for each branch. For example; if you’re working in the Vulcan squad and want to checkout how roundups are looking, easy peasy just go to the dashboard and tap “View Build” on the roundups branch.
What’s also cool about the dashboard is that it has a simple API that the app can use to determine whether it is an out of date build. If the build is out of date, the app will alert the user telling them to update. This is great for ensuring internal testers are always on the latest and greatest.
That’s the end of the road for the internal workflow.
The last stage of the public workflow is to make a public build and upload to AppStore Connect! The CI server also tags the commit with the build number for reference.
There you have it! The system that powers our development cycle from *code code code* to git push and finally a build on the App Store!
P.S. You should totally join us.