My second step into the CI world with fastlane and Android - Tanin's blog

Tanin's blog

App Development | Productivity

My second step into the CI world with fastlane and Android

Posted at — Nov 26, 2019

Some Abstract picture

Fastlane released support for Android in 2015 when it was acquired by Fabric. And as you may know, Google acquired Fabric in 2017. The acquisition surely benefits the fastlane team to improve the integration between their tool and the Android environment to be even more seamless. Today, fastlane is widely adopted across the Android and iOS communities.

In the past few weeks, I’ve been played around with fastlane and got it to publish an app to Play Console. I started from scratch. I had no past experience with fastlane or Ruby, the scripting language fastlane uses. But I’ve got to tell you, it’s so straight forward, yet, so powerful at the same time.

Table of contents:

  1. Ruby crash course for fastlane
  2. Introducing Supply
  3. Setting up with the Play Console
  4. Installing Ruby
  5. Installing fastlane
  6. Initialise your app project with fastlane
  7. Create lanes to deploy your app

1. Ruby crash course for fastlane

On a quick search to find something along the lines of “Ruby for fastlane”, I came across this article, Everything You Need to Know About Ruby for iOS Development from Big Nerd Ranch. Although it says “for iOS Development”, it fits this purpose really well as a lot of third-party tools in the world of iOS development use Ruby: Cocoapods, xcpretty, and of course, fastlane. This article gave me enough knowledge to get started with fastlane.

Here are my short notes from the article:

rvm

Means Ruby Version Manager. It is a command-line tool which allows you to easily install, update, manage, and work with multiple ruby environments.

rbenv

Another tool for managing different ruby environments.

Bundler

In Ruby, libraries or tools are packaged up as gems. fastlane is written in Ruby, that’s why you can install it via the command gem install fastlane.

Bundler is a Ruby gem that allows you to specify all your gems in a file named Gemfile. Think of this one like the dependencies block in your app’s build.gradle where you specify dependencies.

And you can install Bundler by executing gem install bundler as it’s also a gem.

After you installed Bundler, you can install the gems you specified in Gemfile by executing bundle install.

Gemfile.lock

One thing to keep in mind about gems is that they’re tied to specific versions of Ruby. This could introduce the problem of running a project on different machines with different versions of gems. But don’t you worry, Bundler has got us covered.

The first time bundle install is executed on a project, a Gemfile.lock will be created. This is a lock file which maintains the exact version of each gem of the project. So no matter whose machine the project is running on, the gems will always be the same.

And you can just run bundle update to update the versions of your gems.

2. Introducing Supply

Supply is a command-line tool for updating Android apps to the Google Play Store. Basically, it means you can do the publishing from the command line with commands prefixed with fastlane supply .... Supply comes bundled with fastlane, so no extra installation needed. Installing fastlane is covered in part 4. You can read more about Supply here: https://docs.fastlane.tools/actions/upload_to_play_store/. And you may notice that the link ends with upload_to_play_store, this is because “supply” is just an alias for “upload_to_play_store”.

3. Setting up with the Play Console

This step is very well explained on the Supply intro page. Assuming you already have your Google Play Console set, you can just follow the steps below (copied from https://docs.fastlane.tools/actions/upload_to_play_store/)

  1. Open the Google Play Console
  2. Click the Settings menu entry, followed by API access
  3. Click the CREATE SERVICE ACCOUNT button
  4. Follow the Google Developers Console link in the dialog, which opens a new tab/window:
  1. Back on the Google Play Console, click DONE to close the dialog
  2. Click on Grant Access for the newly added service account
  3. Choose Release Manager (or alternatively Project Lead) from the Role dropdown. (Note that choosing Release Manager grants access to the production track and all other tracks. Choosing Project Lead grants access to update all tracks except the production track.)
  4. Click ADD USER to close the dialog

4. Installing Ruby

This guide is for Mac users (sorry Windows users). There are at least two ways to install Ruby on Mac, through brew or just use the pre-installed Ruby on your Mac in which you will only need to install a few tools like rvm and rbenv. I recommend using the pre-installed Ruby as even if you install the new one from brew, fastlane might pick up the old one.

Ruby through brew

  1. brew install ruby
  2. Add export PATH="/usr/local/opt/ruby/bin:$PATH" to .bash_profile or .zshrc if you use zshell.

Mac pre-installed Ruby

If you’re a mobile developer who’s starting from scratch like me, chances are you have never touched Ruby, so you need to update it first.

You can run ruby -v to check the current version of Ruby on your machine.

Before updating Ruby, we first need to install rvm. As mentioned above, rvm helps us manage ruby environments. It is also the standard for upgrading your Ruby installation on macOS

  1. Install rvm
\curl -L https://get.rvm.io | bash -s stable --ruby\curl -L https://get.rvm.io | bash -s stable --ruby
  1. You may get asked to install a public key for the installation. If you do, you can install the key by running:
gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB

  1. Start using rvm
source /Users/Mac/.rvm/scripts/rvm
  1. See the latest available versions of Ruby
rvm list known
  1. Install the Ruby version of your choice
rvm install ruby-<version-number>
rvm install current && rvm use current

5. Installing fastlane

First things first, make sure you have the latest version of the Xcode command line tools installed:

xcode-select --install

There are a few ways to install fastlane

gem install

You can just intall the gem as fastlane is also a Ruby gem:

gem install fastlane

Through Bundler, bundle install

The other way is installing through Bundler as mentioned in the Ruby crash course for fastlane section. To do this, add gem "fastlane" to the Gemfile file as well as specifying the source like this

source "https://rubygems.org"

gem "fastlane"

Then you can install fastlane by running

bundle install

Brew

Or just run

brew cask install fastlane

You can check the version of you fastlane by running

fastlane -v

6. Initialising your app project with fastlane

Now that you’ve got everything ready (the json secret file for connecting to Play Console, Ruby, and fastlane installed), cd to your project and run

fastlane init

You’ll be asked to provide the package name for your app and path to the json secret file. Fastlane will also give you a brief of files that are being generated and what they do.

If it shows an error when trying to run the bundle update command with a suggestion It is likely that you need to grant write permissions for that path. Just continue until it finishes. And then run

sudo bundle update

This command fetches new versions of your gems. But if bundle install hasn’t been executed on the project before, this will act like the bundle install command by fetching the gems specified in Gemfile and create Gemfile.lock to maintain the Ruby versions of the gems of this project. (This also means running sudo bundle install would yield the same results here.)

Looking into your project root folder right now, you’ll see a few new files namely: Gemfile, Gemfile.lock, fastlane/Appfile, and fastlane/Fastfile.

Gemfile

As mentioned, this is where the gems are listed. If you didn’t edit this file before, fastlane would auto-generate this with gem "fastlane" as well as the source where it can be fetched, source "https:rubygems.org".

Gemfile.lock

Although we only have one gem at this point, the fastlane gem depends on loads of other gems. Therefore, if you have a sneak peek on this one, you’ll see a long list of gems, including things like googleauth and google-api-client.

Appfile

This is where you define the configuration information that is global to your app, e.g. Package Name and Path to the json secret file, so you don’t have to enter it every time you run the fastlane commands.

Fastfile

Here live the lanes. A lane is a set of automation script to perform a task. You can create a lane to do things like running gradle commands, screenshots, post an update to Slack, or publish to the Play Store. For example, this is the contents of the auto-generated Fastfile

default_platform(:android)

platform :android do
  desc "Runs all the tests"
  lane :test do
    gradle(task: "test")
  end

  desc "Submit a new Beta Build to Crashlytics Beta"
  lane :beta do
    gradle(task: "clean assembleRelease")
    crashlytics

    # sh "your_script.sh"
    # You can also use other beta testing services here
  end

  desc "Deploy a new version to the Google Play"
  lane :deploy do
    gradle(task: "clean assembleRelease")
    upload_to_play_store
  end
end

First of all, the platform is specified. Then, in the android platform block, there are three lanes, test, beta, and deploy.

The test lane just runs the gradle command test. This is equivalent to executing gradle test from the command line.

The beta lane runs gradle clean assembleRelease and upload a new build to Crashlytics Beta.

And the deploy lane runs gradle clean assembleRelease and upload to the Play Store.

The commands in lanes are called fastlane actions. You can find out more about all the available actions at https://docs.fastlane.tools/actions/.

metadata

If you entered “y” when it asked whether to download existing metadata, you’d also see a folder called metadata. If you have published your app before, all the metadata, app descriptions, screenshots and images, changelogs, title, and video, will be downloaded into this folder. This is where you’ll edit your metadata, add a new version changelog, or update some screenshots so that these get changed automatically for your apps without touching the Play Console.

8. Publish your Android apps

With the default lanes specified, you can run bundle exec fastlane, it’ll ask you which lane you want to run. You can have a go with the test lane to see the magic. You can shortcut the lane picking step by suffixing your command with the lane name like this.

bundle exec fastlane test

When I ran bundle exec fastlane deploy, there was a problem with my Keystore file location. I didn’t keep my Keystore file at root. And for some reason, fastlane couldn’t find my Keystore file even though Android Studio could if I try to build a release from it. So I moved my Keystore to root, gitignored it, and update the signingConfigs block in app/build.gradle to the following:

android {
     signingConfigs {
         release {
             keyAlias 'app_key_alias'
             keyPassword 'app_key_password'
             storeFile rootProject.file('app_key_store.jks')
             storePassword 'app_store_password'
         }
     }
...
}

Before, the storeFile was something like

storeFile file('/Users/Mac/.../app_key_store')

Now to the steps I think would apply to everyone.

1. Update app version

If this is an update to your existing app, make sure you don’t forget to update your versionCode and versionName to new ones.

2. Update changeLogs

You won’t get an error if you forget this, but it can cause confusion. If you’ve pulled metadata from the existing listing on Play Console, you’ll see a text file under a similar location to this fastlane/metadata/android/en-GB/changelogs. The text file should be named the same as your versionCode. So make sure you update the file name and the content of the file to be the updates you want under the What’s new section of your app on Play Store. e.g. 20.txt

3. Update lane

Lastly, the deploy lane is needed to be updated with the release credentials. It seems redundant as we already have these credentials specified in app/build.gradle - signingConfigs/release. But I kept getting this error:

Google Api Error: apkNotificationMessageKeyUpgradeVersionConflict: APK specifies a version code that has already been used. - APK specifies a version code that has already been used.

And adding the credentials fixed it.

desc "Deploy a new version to the Google Play"
lane :deploy do
	gradle(
		task: "clean assembleRelease",
		properties: {
	  		"android.injected.signing.release.file" => "key-store.jsk",
			"android.injected.signing.store.password" => "H63JAV",
			"android.injected.signing.key.alias" => "key_alias",
			"android.injected.signing.key.password" => "KEYPASSWORD"
		}
	)
	upload_to_play_store
end

And now you can just run bundle exec fastlane deploy, brew some tea, and wait for Google Play confirmation email that your update has been released 🎉

If you have any questions, please ask me at @landtanin. Also, I’m clearly new to this world of fastlane and automation. So if you have comments or suggestions, please let me know. Thanks for reading!