Deploy React Native apps using fastlane
In this blog, we will learn how to use Fastlane and Github Actions to deploy React Native apps on the Play Store.
Setting up fastlane
Fastlane is an open-source platform aimed at simplifying Android and iOS deployment. It lets you automate every aspect of your development and release workflow.
Your workflow will be defined by two Ruby files, called Appfile and Fastfile.
The Appfile stores useful information that is used across all Fastlane tools like your Apple ID or the application bundle identifier, to deploy your lanes faster and tailored to your project needs.
The FastFile contains the lane definitions. Lanes are functions that define actions to execute in a sequential way.
To install it on your machine, follow these steps in the Fastlane documentation: https://docs.fastlane.tools/getting-started/android/setup/.
To be sure that Fastlane is correctly installed, we can use fastlane --help
in the terminal.
Create a Google Developers service account
The Google Developer Services will be used to upload an Android App Bundle on the Play Store.
Follow these steps in Fastlane documentation: https://docs.fastlane.tools/actions/upload_to_play_store/#setup
After completing these steps, you should have a credentials JSON file that looks like this
{
"type": "...",
"project_id": "...",
"private_key_id": "...",
"private_key": "...",
"client_email": "...",
"client_id": "...",
"auth_uri": "...",
"token_uri": "...",
"auth_provider_x509_cert_url": "...",
"client_x509_cert_url": "..."
}
Generate a Keystore file in order to sign in the app
We are using Android App Bundles, we need to sign the app bundle before we upload it to the Play Console, and Play App Signing takes care of the rest.
Generate the release.keystore file. The three values we need in this file are
- The key alias (see the keytool command below).
- The keystore password.
- The key password.
$ keytool -genkey -v -keystore release.keystore -alias <your key alias> -keyalg RSA -keysize 2048 -validity 10000
For more information, go to https://developer.android.com/studio/publish/app-signing#generate-key
Before continuing, make sure you have:
- Fastlane is installed and working.
- A Google Developer Service account, as well as your json credentials file.
- A release.keystore file.
Setting up Fastlane
From now on, we will consider that the Android configuration files are generated by React Native init without changes.
Here is how we can initialize a React Native app:
$ npx react-native init AwesomeProject
In the Android folder, run
$ fastlane init
- Provide the package name for the application when asked (e.g. com.my.app).
- Press enter when asked for the path to your JSON secret file.
- Answer 'n' when asked if you plan on uploading info to Google Play via Fastlane (we can set this up later).
A fastlane folder has been created and contains two files:
- Appfile which defines global configuration information of the app.
- FastFile which defines lanes of Fastlane.
The service_account.json file of your Google Developer service account will be used in AppFile. Copy the json file inside the Fastlane folder and modify android/fastlane/AppFile
json_key_file("fastlane/service_account.json")
package_name("com.my.app")
You can now test the connection to the Google Play Store
$ fastlane run validate_play_store_json_key json_key:/path/to/your/file.json
Setting up fastlane plugins
Plugins allow reusing functions created by the community.
We're going to add a Fastlane plugin that can get the application's version number from the package json file that will define the version number of the Android App Bundle. For this, we need to add the load_json
plugin:
$ fastlane add_plugin load_json
> Should fastlane modify the Gemfile at path
'/xxx/react-native-app/android/Gemfile' for you? (y/n)
> y
Each AAB or APK must have a unique version code in order to be uploaded to the Playstore. To set the version code of your app, install the versioning_android
plugin
$ fastlane add_plugin versioning_android
Now let's configure the first lane in the FastFile file. The first thing to do is to implement a function that generates a unique versionCode for the Android App Bundle. Put this function, that generates a new version code, in the upper part of the android/fastlane/FastFile
.
default_platform(:android)
def getVersionCode
# Instead of managing the version code manually it is based on a timestamp in seconds
# Any build done more recently is considered to be a higher version
# versionCode increase every minute (so max 1 build per minute)
# versionCode cannot be smaller than legacyVersionCode
thirtySeptemberTwentyTwenty = 1601480940 / 60
legacyVersionCode = 10902
versionCode = legacyVersionCode + (Time.now.to_i / 60) - thirtySeptemberTwentyTwenty
if versionCode > 2100000000
raise "versionCode cannot be higher than 2100000000"
end
versionCode.floor()
end
And define the first lane that will use getVersionCode to update android.defaultConfig.versionCode
in the app/build.gradle
file
platform :android do
desc "Increments internal build number tracking (different than version)"
lane :bump_build_number do
android_set_version_code(
version_code: getVersionCode()
)
end
Still in android/fastlane/FastFile
, you can define the lane for building and uploading the Android App Bundle on the Playstore.
desc "Build and uploads the app to playstore for a internal testing release"
lane :playstoreInternal do |options|
# Retrieve version of my app in package.json (React Native case)
package = load_json(json_path: "../package.json")
# Clean build folder
gradle(
task: "clean"
)
# Bump android.defaultConfig.versionCode
bump_build_number
# Do the bundle of the application
gradle(
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.signing.store.file" => Dir.pwd + "/release.keystore",
"android.injected.signing.store.password" => options[:RELEASE_KEYSTORE_PASSWORD], # keystore password
"android.injected.signing.key.alias" => options[:RELEASE_KEYSTORE_ALIAS], # alias
"android.injected.signing.key.password" => options[:RELEASE_KEYSTORE_KEY_PASSWORD], # key password
"vname" => package["version"]
}
)
# Upload Android App Bundle to PlayStore like Internal testing Release
upload_to_play_store(
track: 'internal',
release_status: 'draft', # <http://docs.fastlane.tools/actions/upload_to_play_store/#parameters>
skip_upload_apk: true,
version_name: package["version"]
)
end
Let's take a closer look at the lane.
First we retrieve the application version from the package.json file.
desc "Build and uploads the app to playstore for a internal testing release"
lane :playstoreInternal do |options|
package = load_json(json_path: "../package.json")
Then clean up the build folder and use bump_build_number lane created above
gradle(
task: "clean"
)
bump_build_number
Finally, we define a gradle task to build the application in app/build.gradle
.
gradle(
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.signing.store.file" => Dir.pwd + "/release.keystore",
"android.injected.signing.store.password" => options[:RELEASE_KEYSTORE_PASSWORD], # keystore password
"android.injected.signing.key.alias" => options[:RELEASE_KEYSTORE_ALIAS], # alias
"android.injected.signing.key.password" => options[:RELEASE_KEYSTORE_KEY_PASSWORD], # key password
"vname" => package["version"]
}
)
Gradle needs these four arguments to sign your application:
android.injected.signing.store.file
: Path to the release.keystore file.android.injected.signing.store.password
: Keystore password.android.injected.signing.key.alias
: Key alias of the release.keystore file.android.injected.signing.key.password
: key password.
Three of these properties are sensitive, so they can be filled through the Fastlane parameters. For example:
$ fastlane playstoreInternal RELEASE_KEYSTORE_PASSWORD:**keystore password**
At this step, we can check if the application is properly bundled. Copy your release.keystore
file in the android/fastlane
folder and run the command below with the passwords and the alias of your release.keystore
file
$ fastlane playstoreInternal
> RELEASE_KEYSTORE_PASSWORD:**mypassword**
> RELEASE_KEYSTORE_ALIAS:**myalias**
> RELEASE_KEYSTORE_KEY_PASSWORD:**mykeypassword**
The last step of the lane is to upload the aab on Playstore
upload_to_play_store(
track: 'internal', # 'internal' for internal testing release
release_status: 'draft', # http://docs.fastlane.tools/actions/upload_to_play_store/#parameters>
skip_upload_apk: true, # if you want to upload apk, use skip_upload_aab instead
version_name: package["version"]
)
It's time to test the whole workflow of our awesome lane
$ fastlane playstoreInternal
> RELEASE_KEYSTORE_PASSWORD:**mypassword**
> RELEASE_KEYSTORE_ALIAS:**myalias**
> RELEASE_KEYSTORE_KEY_PASSWORD:**mykeypassword**
You should be able to see the app build in Google Play Console.
Congrats! You just deployed your react native build on Play Store.