Deploying Apps to QA using Fastlane and Jenkins

From downloading the software to implementing it.
Christian Aranda
Christian Aranda
March 26, 2018

Following on from my last post in the series, on how we develop software with maximum efficiency and minimal interruptions, I’m going to explain how we set up both Jenkins and Fastlane to deploy builds to our QA department.

Configuring a Continuous Integration server can be a little tricky the first time around. As with everything you do in life, it takes time and patience to master and feel comfortable with. In this post, I will detail how we set up our pipeline in order to deliver builds to QA with ease for every feature that we develop.

Our approach may not fit your specific needs but you can use it as a base to experiment with before configuring it to suit your requirements. If you have any queries, I’m an email away.

A few notes before we start:

  • Our deployment process is automated so it doesn’t need any human intervention to trigger it.
  • We get the changelog from the Git commits.
  • Fastlane modifies and increments build number and pushes commits to the remote repo.
  • We get notifications if the process fails or succeeds.

Install Jenkins

Installing Jenkins is pretty straightforward, thanks to their useful User Documentation, which gives users access to handbooks, tutorials and guided tours.

Install Fastlane

Similarly, Fastlane offers a whole host of setup docs to get you started.

However, I do have a top tip for you! I’ve been experimenting with Apple’s system Ruby and I would advise that, in this instance, you opt for Ruby Version Manager instead. Ruby Version Manager installs each version of Ruby straight into a hidden folder in your home folder so it doesn’t affect the system Ruby. There’s a helpful installation guide online if you’re looking to follow suit.

When using RVM and RubyGems to install Fastlane, I found that the Jenkins job was not able to find Fastlane so I installed the Jenkins RVM plugin and specified my Ruby version like this…

Image credit

Set up Jenkins Job: Downloading and setting up the application

Our basic job configuration checks out our Git project using Jenkins Git plugin. It then executes the appropriate lane of Fastlane.

We use Bitbucket so we first need to set up a Cron job that will trigger a build in Jenkins whenever a PR is opened in Bitbucket. In order to do that, we installed the Bitbucket pullrequest builder.

This is our configuration:

CRON: Use * * * * * and it will poll Bitbucket every minute to check for PRs.

Credentials: Set up a shared credential that you can use across all projects.

Repo Owner & Name: You should take the data from your repo URL/SSH, for example:

git@bitbucket.org:REPO_OWNER/REPO_NAME.git

Branches Filter: You can choose to only build specific branches or leave it blank to build all.

CI Identifier & Name: We use Jenkins as our CI identifier and Project Name.

Fastlane Configuration

I’m now going to detail the Fastlane actions defined in the Fastfile. We use Fabric to distribute our internal builds to QA, so I defined a Lane to build and deploy to Fabric. An environment variable with the Fabric API key is set.

fabric_api_token = ENV[‘FABRIC_API_TOKEN’]
fabric_ build_secret = ENV[‘FABRIC_BUILD_SECRET’]

We don’t want to accidentally deploy uncommitted changes so we also checked the Git status.

ensure_git_status_clean

Keeping track of deploys

Before we build, we need to acquire the version number and increment the build number. We enable Fastlane to handle automatic version incrementing for us. When doing so with an iOS Application, we use Apple Generic Versioning. There’s plenty of advice online as to how to set up automatic build increments so the process is relatively painless.

version_number = get_version_number
build_number = increment_build_number
complete_version_number = “#{version_number}/” + “#{build_number}”
release_notes = change_log_since_last_tag

Creating the build

It uses a scheme for deployment and also handles signing for us.

get_certificates  # invokes cert
get_provisioning_profile  # invokes sigh
build_app(workspace: “Ezample.xcworkspace”, scheme: “Example Dev”)

Shipping to Fabric

With our build finished, we then upload it to Fabric.

crashlytics(
api_token: fabric_ build_secret,
build_secret: fabric_ build_secret,
groups: ‘inshur-testing’,
notifications: true,
notes: “#{release_notes}”
)

Time to notify

And now that our build is deployed, it’s time to notify the team that there’s a new version available. We created a new Slack web-hook and added an environment variable that means, when a build passes,  a notification gets posted to Slack to let everyone know that there is a new version available.

If the lane fails, it will also post to Slack. To ensure we keep on top of it, during the development process, we made the following adjustment:

slack(
message: “<!here|here>: New iOS – Inshur Dev β v#{complete_version_number}\n has been submitted to Fabric :rocket: \nRelease notes:\n #{release_notes}”,
success: true,
payload: {
“Build Date” => Time.new.to_s,
“Built by” => “Jenkins”,
},
default_payloads: [:git_branch, :git_author],
attachment_properties: {
fields: [{
title: “Changelog”,
value: “#{release_notes}”,
short: true
}]
}
)

Before committing the changes we’ve made to the changelog and Info.plist files, we need to clean any build artefacts. This includes the actual binary that was compiled, as well as the unit test coverage reports, and the downloaded provisioning profiles.

clean_build_artifacts

Conclusion

Jenkins is an open source automation server that is flexible enough to meet our specific needs, allowing us to scale as needed, meaning we can add as many jobs as we want without having to upgrade the price plan.

We did, however, once lose Jenkins configuration, so I encourage you to put the Config file under source control! Fastlane is now developing its own CI which I will be keeping an eye on in order to test it as soon as it’s released. Keep your eyes peeled for a future blog post on that!

Automating our deploys helps us to do more of them, as it reduces the complexity of testing and helps us to uncover bugs promptly. It has been extremely useful across the board, in all of our projects, and it’s something we recommend.

(image credit)