Choosing the right CI system is always hard. You're often looking at other teams for inspiration and take the usual approach. As an iOS developer of the Collect by WeTransfer app I did exactly the same when we started setting up our CI system. In the end, we shut down Travis and Jenkins in less than a day of work.
Guest post by Antoine van der Lee, Lead iOS Engineer at WeTransfer.
As a Lead iOS Engineer at WeTransfer, Antoine’s work is focused on code architecture and team processes. He's passionate about contributing to the iOS community where you might know him from his weekly blog posts on his personal blog called SwiftLee. He particularly enjoys speaking on best practices for structuring code architecture in a way that creates sustainability, as well as open sourcing frameworks and how iOS developers can be more successful in their work.
Our very first approach
We started in early 2017 with a Travis-only approach. Most of our tech teams were using it so it made total sense for us as well to do the same.
It didn't take a long time before we started to notice that Travis was not the fastest solution for us as a lot of concurrent builds delayed ours. Reason enough for us to set up our own Jenkins machine with a classic Mac mini.
We ended up with the following setup:
- Every day, a new TestFlight build was delivered using Travis
- Travis was also used to automatically release a new app update on every Monday
- Jenkins was used to run tests for every pull request
Maintenance and missing knowledge
Although it all worked fine, we ended up spending quite some time on maintenance. Jenkins is great, mostly works fine, and gives a lot of freedom. The downsides, however:
- Plugins often require updates with risk for stability
- Xcode needs to be manually updated
- It's not clear how it's setup without specific Jenkins knowledge
Taking into account we also still use Travis for our daily and weekly builds you can imagine we lost quite some time to maintaining those configurations.
Open source projects
Although I'm not going to dive into our setup too much in this blog post I need to share that we also used Travis for our open source projects. We have quite a few and they all share the same configuration. However, each configuration is set up individually within each repository.
This was a great improvement but couldn't take away the need for a single centered CI solution which makes this all accessible from within an online dashboard.
Bitrise to the rescue
Funny enough we never realized that the solution could be closer than we thought. Our Android team has been on Bitrise for a while and had great experiences setting it up. Furthermore, it seemed that Bitrise was the answer to all the things we missed.
Why we took Bitrise as the solution
Looking at our previous implementation with Jenkins and Travis we concluded that Bitrise filled our needs.
- Bitrise has the reputation to deliver support for new Xcode betas within 48 hours
- It has a great user interface
- The workflows are easy to understand for everyone in the team
- Workflows can be shared and allow us to easily maintain all our projects including the open-source ones
- It's been greatly respected by the community and listens to the needs of the community
And these are only partly reasons for us to switch over as we, for example, didn't even mention they have open-source implementations for workflow steps!
The first step: making the same setup work
The day we started to switch over to Bitrise we had one goal in mind. We basically were aiming to match our current implementation as soon as possible.
First, make it work, then make it shine
We have this workflow in our day to day programming as well: First, make it work, then make it shine. It allows you to move fast without jumping on new stuff. Once it works, you can start replacing steps and optimize them for Bitrise specific features.
We started with matching our environment. This couldn't be easier with the Stack configuration page:
This made sure that our tests should be able to succeed as they did as well on our current Travis and Jenkins stack.
Matching the workflows
Our workflows are quite similar and only diverse by a single Fastlane lane. It was clear to us that we could use a primary workflow in Bitrise and share it for our daily, weekly and PR builds.
Therefore, we started creating our primary workflow and we did that in the simplest possible way.
It basically did two things:
- Check out the project (you get these steps for free from Bitrise)
- Run our Fastlane lane test
After setting up our secrets for tools like Danger and Fastlane Match we set up a simple trigger to run this workflow on every PR:
And that was it, enough to match our current Jenkins implementation and the moment we shut down Jenkins 🎉
Sharing the primary workflow
The last needed step was to share this primary workflow with the daily and weekly build workflow. The only thing that had to change was the Fastlane lane to run.
We solved this by creating an empty workflow for both the daily and weekly workflow and setting an environment variable for the Fastlane lane to run.
We could then simply reference this environment variable from our Fastlane step in our earlier showed primary workflow and we were all set to set up the scheduled builds!
Our last step was to schedule our daily and weekly builds. This would allow us to also shutdown our Travis implementation. Do note that at this moment we were only 4 hours working with Bitrise without previously ever working with it!!
You can schedule builds easily with the Start/Schedule build button. Scheduling builds at the moment of writing this blog post was still in beta but didn't feel like that at all.
First up was our daily build delivery for which we created the TestFlight_Release workflow.
We set the time to 19:00, selected all working days, and finally selected our workflow. This was all we needed to do!
We did the same for our weekly release build on every Monday at 11:00 using the. App_Store_Release workflow and we were all set.
Tips that helped us move fast
In the end, we shut down Travis and Jenkins in less than a day of work. A few things helped us to move fast and might help you to speed up as well.
Build up the workflow
We disabled running our tests at first so we could first test the basics of our Fastlane implementation. CI runs would otherwise take up more time to run all tests every time. Finally, we let CI run only a part of our tests so we could iterate quickly and validate our implementation and changes. Only at the very end, we would enable all our tests to see whether it all had worked.
Triggering a rebuild
The build trigger button brings up a form to trigger a build manually.
We used this quite a lot to trigger a build from the custom feature branch we created for setting up Bitrise. It's an easy way to manually trigger a build without pushing anything unneeded to your branch to trigger a build.
Available environment variables
Bitrise comes with a great list of environment variables ready for you to use. We needed a few like BITRISE_GIT_BRANCH and BITRISE_PULL_REQUEST which we could easily get from this list.
It's still early days for us at WeTransfer with Bitrise but we're already quite into it. We no longer make use of Travis and Jenkins and we're working on optimizations for our current workflows.
For that, more to come! I'll share my learnings in another post, right here at Bitrise. Keep an eye open and in the meantime, if you have any questions, reach out to me on Twitter or follow my personal blog SwiftLee for more workflow and Swift related blog posts.