Hotwire Tech Blog

Scribes from Hotwire Engineering

Goal

At Hotwire one of our goals is to create processes and tools that help deploy new features in iOS apps as often as possible with high quality. In other words, we want continuous deployment for iOS apps. To get there we have two main challenges – one is regression time and another is deployment time. In this blog we will focus on how we reduced deployment time to App store from hours to minutes with the help of Fastlane & Jenkins. It took hours because there are several sub tasks involved like creating a release build, uploading to iTunesConnect, uploading binary & dSYM to Fabric (Crashlytics) for crash monitoring, enabling beta testing for external vendors, etc. Fastlane provides capabilities to automate these tasks and Jenkins helps us configure and schedule the deployment.

Introducing Fastlane

Fastlane is a toolchain that mainly helps in automating the process of submission to iTunes connect. Furthermore different “lanes” can be configured to perform different operations like running unit tests, distributing debug builds to testers, and managing provisioning profiles on the build machines. In summary, different pipelines (or lanes ) can be configured for different environments. To give an example, if you want to distribute a debug build to your testers via crashlytics, a custom lane called “distribute” can be created to complete the processes involved in distribution.

How it works

Fastlane is a ruby gem installation. After initialization of Fastlane against the project, four configuration files are created under the folder named fastlane, namely – AppFile, DeliverFile, Fastfile and GymFile. These are ruby configuration files which are used by the toolchain at runtime.

  1. AppFile: For teams that have different application identifiers for different environments can be configured in this file. For example, the following code will set the app identifier for the lane ‘ios beta” to “com.myapp.identifier”. This is a more advanced configuration. Under normal circumstances, it might be just empty.
    for_lane "ios beta" do
      app_identifier "com.myapp.identifier"
    end
  2. DeliverFile: This file is mostly about configuration of properties related to iTunes upload like default_language, title, description, version, keywords, copyright etc. The exhaustive list can be found here https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
  3. FastFile: This is the file where are the lanes/pipelines are defined. There are a number of useful ruby scripts built-in to the toolchain. These scripts are called Actions in the context of Fastfile. For more pre-built actions please refer to https://github.com/KrauseFx/fastlane/blob/master/docs/Actions.md. We can also create custom actions using this guide https://github.com/KrauseFx/fastlane/blob/master/docs/Advanced.md. The actions specified in the lane are executed sequentially and can include custom shell scripts as well.
  4. GymFile: This file specifies the parameters to be used by xcode command lines tools to create an ipa.
    workspace "MyProject.xcworkspace"
    output_name "MyProject"
    configuration "Release"
    clean true
    scheme "TargetName"

     

Following Fastfile can give a brief overview of what we at Hotwire are doing in our deploy lane. Fastlane performs the following actions  – increment version and build number, update the deployment provisioning profiles for all the schemes, archive the ipa, upload to crashlytics, commit the version changes, tag the commit, push these changes to remote.

# Make sure we start off with a clean repository state. This is necessary, since after the version increment, there might dirty files which we don’t want to end up with in the version bump commit.

ensure_git_status_clean

# Automatically increment version number. bump_type can be patch, minor, major.
increment_version_number

# Increment build number to current date.
build_number = Time.new.strftime("%Y%m%d%H%M")
increment_build_number build_number: build_number

# Download and update latest provisioning profiles for the given app identifiers
sigh({
  app_identifier: "com.your.app.id",
})

# Archive and generate ipa
gym({
  configuration: ENV['CONFIGURATION']
})

# Upload ipa to iTunes Connect as pre-release
testflight(skip_submission: false)

# Upload ipa to Crashlytics
crashlytics({
  crashlytics_path: './ThirdParty/Crashlytics/Crashlytics.framework',
  api_token: 'your_api_token',
  build_secret: 'your_build_secret',
})

# Send HipChat success message
hipchat({
  message: "Successfully uploaded Hotwire v#{version_number}(#{build_number}) to iTunes Connect!",
  channel: "channel_name",
  success: true
})

# Make sure our directory is clean, except for changes Fastlane has made

    clean_build_artifacts

# Commit version increment to the current branch
    commit_version_bump(
       message: "Versio n Bump From Fastlane Hotwire v#{version_number}(#{build_number})",
       xcodeproj: 'Project.xcodeproj',
    )

# Add release tag to the commit. Example of the this format – v5.1(201506061120)
    git_tag = "release/v#{version_number}(#{build_number})"
    add_git_tag tag: git_tag

# Push version bump to remote
    push_to_git_remote

# Send HipChat success message
    hipchat({
       message: "Successfully deployed Hotwire v#{version_number}(#{build_number})!",
       channel: "channel_name",
       success: true
    })

Conclusion

Without Fastlane, it takes significant time to release to the app store. Furthermore, presence of a developer is a necessity, since it involves the process of figuring out the app store provisioning profiles for the app store, tagging the git branches and pushing these changes to remote. To make the job even more easier, we have created a Jenkins job where you just need to specify the build parameters – the release branch and what is the bump type. With the combination of Fastlane and Jenkins Job, anyone can now release the app to App Store.  Another advantage is we can use Fastlane to take snapshots of the app, frame it in an iPhone and submit. If there are any visual updates to the app in the release, the screenshots of the app can updated to iTunes Connect automatically. Furthermore, if we decide to do biweekly builds to the App Store, it would just be a matter of adding few more lanes and achieve continuous deployment.

Leave a Reply

Your email address will not be published. Required fields are marked *