CI/CD of mobile apps on bitbucket and bitrise

Nicolas Lagier
Healint-Engineering&data
4 min readMar 12, 2021

Ideal workflow

We have 3 main stages in our development workflow. Those 3 environments map to 3 git branches.

Dev: This is where the development happens 🧑‍💻👩🏻‍💻. This should not be visible beyond the developers team. And might be a bit unstable.

Test: This is an environment uniquely for internal consumption . It should be very stable. And this is where the QA testing happens before release to production.

Production: This is where we find the app which is deployed to production for millions of users 📱. Better be very stable.

For dev and test environments we like to use firebase as it provides a very seamless way of distributing the app internally and track immediately the crashes and the performances of those releases.

For production, we want to push directly to testflight and playstore, so that the a release is just a click away.

Needless to say that all of that should be automated. Anytime code is merged to one of those branches.

Our apps are partly native (java/kotlin and objective-c/swift), partly react native (typescript) so this complicates slightly the build process.

Bitbucket pipelines for android

Our code is hosted on Bitbucket so going toward Bitbucket pipelines was a natural choice. It is configuration file based. It runs on docker images. You can run multiple steps in parallel. Each step can run on a different docker image. You can pass artifacts from one step to the next. And the caching mechanism is fairly efficient.

Uploading to firebase

Very easy using firebase ci tool.

You need to have a FIREBASE_TOKEN environment variable as per the cli documentation.

Uploading to the store

A bit more cumbersome to setup. But following this documentation from the Bitbucket blog, we used gradle play publisher. And once all this setup and the related keys and tokens and variables are setup, the step looks very simple:

Unique version number

Having unique version number is important when uploading to the play store so we want to make sure that the version number is dynamic. This done by adding the following to our build.gradle file.

Only build when necessary

Using the conditions instruction we are rebuilding android app only if the react code or the android code is modified.

In the above sample, note the <<: *buildRelease, this is a yaml anchor, which allows defining the step only once and reusing it in multiple pipelines. (less copy/paste).

Bitrise for iOS

IOS requiring OSx to build, there are no docker machine that could do the job. Therefore Bitbucket pipelines is out of the question. So, we turned to bitrise.io. There is a fancy dashboard. But the configuration is ultimately file based. It can be hosted directly on the repo or just on bitrise. The configuration is fairly powerful and it allows defining workflows, that can in their turn nest other workflows.

It relies on a fairly extensive set of open source steps. And if by any chance you do not find a step that fits your needs, you can always default to the good old bash script step. (we did not have any where we add to use bash script)

We linked our apple developer account with bitrise.io and it takes care of handling all the certificates mess.

Bitrise also comes with a fairly powerful CLI which can be installed with brew install bitrise. This cli allows eventual testing some flows locally. It also allows launching a local version of the dashboard using the following command:

bitrise :workflow-editor

Uploading archive

For firebase it is quite straightforward using the dedicated step.

And for the testflight upload there is also dedicated step. For that step, you just need to make sure that your archive step built using the “app-store” mode.

Unique version number

Use dedicated step.

Next steps and after thoughts

Currently we built into this workflow linting of android and ios code. There are also a few android unit tests. But, in the coming months we intend to ramp up significantly the automated tests. The goal is to have much more confidence, whenever we press the release button🟢..

When reading the above, it feels like using bitrise was easier than bitbucket, which might be true. But bitrise is more extensive. So why are we not using bitrise for everything? Well, we are using Bitbucket for the rest of our backend services, it is fairly easy to manage and the cost is very reasonable. So, for now we prefer keeping bitrise as the exception.

Saying that setting up this whole workflow was super smooth would not be entirely true but, most of the issues that we encountered were mostly related to legacy issues and we were trying to retrofit different things into it. If we had done this from day one, it would probably have been much smoother. And the truth is that we removed a big operational risk.

  • No more day spent at the end of each iteration to “prepare the release”.
  • No more list of manual steps to be performed to “prepare the release”.
  • No more chance of “the release certificates” are on dude A’s machine but he is off today…

--

--