This is Part Two in a four-part developer series by Evan Glazer.
Series Abstract
Finishing the application build processes is normally very slow for many organizations, particularly with mobile platforms that need to use very specific criteria. In this developer series, building an application in React Native won’t be our primary goal; building processes that give us the ability to build, utilize CI/CD mechanisms through a pipeline and deploy (CD) to different environment types for testing and publishing to the app stores in React Native will be the main theme. It will be imperative that we start by outlining the tools and technology we will need to follow in this part. It’s fair to say that React Native is a fast-moving framework and most of the blog posts I have read seemed outdated and lead me down some unnecessary rabbit holes. This post will in time also become outdated, so be aware these instructions were helpful as of:
Fastlane 2.137.0
React Native 0.59
Expo SDK 35
XCode 11.0
In part one, we began by setting up our project to read from environment variables and initialized our project with GitHub. We will continue to work through increasing the functionality to inevitably be able to move from a static type of build to a dynamic build process, increasing the build numbers automatically for the application version. We will work toward being able to deploy from our local computer using Fastlane in this article before we introduce remote tools to automate the process with triggers. The following architecture will allow us to take care of development and production environments, which will enable us to push to the app store and beta/test-flight on the iOS and Android stores.
Fastlane Time!
An open source platform aimed at simplifying Android and iOS deployment. fastlane lets you automate every aspect of your development and release workflow.
Fastlane contains numerous tools, but we’ll just be using match, gym, pilot, supply and fastlane itself. To install run the following gem from the command line:
gem install match gym pilot supply fastlane
iOS Implementation
Here we have our bundle identifier as com.version.control
and semantic versioning labeled as 1.0.0(1)
, but this will be different for your specific application to launch to the app store (please change accordingly).
We need to set the Code Signing Identity under Build Settings to the correct identities. For Debug, set it to iOS Developer and Release set it to iOS Distribution.
Fastlane Lanes
Make sure you cd into the iOS folder (cd ios
). fastlane init
Helping run through the options, I chose to have Fastlane automate some of the files for iOS app store distribution by selecting option #3 – Automate App Store distribution
. It will then ask you to add your Apple ID developer credentials after it has obtained your available schemes and app identifier that we just set. After supplying our App Store credentials, it will update the bundle, and then we’re ready to see what was generated. You can also have it generate the application on the store if you don’t want to manually create that later. Let’s cd
into our ios directory and then fastlane folder and we should see our Appfile
and Fastfile
. The Appfile
includes information about the ios application:
app_identifier("com.version.control")
The Fastfile
includes information about the build process and what each “lane” will accomplish:
default_platform(:ios)
And now if we try the command fastlane release
on the project to test that things are uploading correctly, we should see something like the following:
Development Time!
Let's focus on Code Signing.
When deploying an app to the App Store, a beta testing service or even installing it on a single device, most development teams have separate code signing identities for every member. This results in dozens of profiles including a lot of duplicates. You have to manually renew and download the latest set of provisioning profiles every time you add a new device or a certificate expires. Additionally, this requires spending a lot of time setting up a new machine that will build your app. We’re going to utilize Fastlane's match
to create one central repository for the application. Let's start by creating a new Apple ID for the team (e.g. ios-dev@company.com) and use that from now on.
Now we will do match init
and begin to provision our certificates
You can continue by adding the certificates for Production by using match appstore
and Development by using match development
. When you’re finished running those commands, your GitHub repo should look like the following:
We’re getting close but first a re-visit to XCode because now we’ve got those provisioning profiles from match, we need to tell XCode about them as we had turned off Xcode’s ability to automatically manage signing. Select your project target and under the general section select your match AppStore provisioning profile. Now that we have match setup we can begin to alter our lanes so that when we can build with the latest certificates. We will add match to our production (release) and development (beta) lanes. Updates to Fastfile
:
default_platform(:ios)
Android Configuration
Before we implement Fastlane into our Android project, it is imperative that we set up our keystore so we can publish to the Google Play Store. We can start by changing directory into our android/app folder by running cd android/app
from our project directory. Then we will utilize Keytool; which will help us generate our keystore file – you will want to answer all the questions the tool generates:
keytool -genkey -v -keystore release-key.keystore -alias version-control-alias -keyalg RSA -keysize 2048 -validity 10000
Now we can add to our gradle.properties
file the keystore file, alias, and password; which will be used in our android config for signing. Note: you may need to modify these variables if you had used different key names, etc.
gradle.properties:
RELEASE_STORE_FILE=release-key.keystore
Now, let's go to our android config in our build.gradle
and modify our signingConfigs.
android { compileSdkVersion safeExtGet("compileSdkVersion", 28) compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig { applicationId 'com.version.control' minSdkVersion safeExtGet("minSdkVersion", 21) targetSdkVersion safeExtGet("targetSdkVersion", 28) versionCode 1 versionName '1.0.0' multiDexEnabled true testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // Deprecated. Used by net.openid:appauth manifestPlaceholders = [ 'appAuthRedirectScheme': 'host.exp.exponent' ] } dexOptions { javaMaxHeapSize System.getenv("DISABLE_DEX_MAX_HEAP") ? null : "8g" } signingConfigs { debug { storeFile file('../debug.keystore') } release { storeFile file(RELEASE_STORE_FILE) storePassword RELEASE_STORE_PASSWORD keyAlias RELEASE_KEY_ALIAS keyPassword RELEASE_STORE_PASSWORD } } buildTypes { debug { debuggable true ext.enableCrashlytics = false } release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' consumerProguardFiles 'proguard-rules.pro' signingConfig signingConfigs.release } }
Now let’s make sure we can build our project and assemble the release. First, we need to run expo publish
to generate release type configuration files Lets go back into our android directory (cd android
) and run ./gradlew assembleRelease
. Or you can check by going back into our android project on android studio and change the build variant to release
and build and run the application. This should give us a result as below:
Your APK will appear in the android/app/build/outputs/apk
folder.
Android Fastlane Time!
First, we need to init fastlane in our android project to create our lanes, etc. It will ask for you to set up Fastlane Supply, but skip this for now as we will set this up in the next steps.
cd android fastlane init
This will create two files: Fastfile
in the android directory:
default_platform(:android) platform :android do desc "Submit a new Beta Build to Crashlytics Beta" lane :beta do gradle(task: "clean assembleDebug") crashlytics end desc "Deploy a new version to the Google Play" lane :deploy do gradle(task: "clean assembleRelease") upload_to_play_store end end
Appfile
in the Android directory:
# json_key_file()
Time to build and submit to the store!
Supply uploads app metadata, screenshots, binaries and app bundles to Google Play. You can also select tracks for builds and promote builds to production. Before we begin to init supply, we need to set up the JSON key which allows the automation to upload to the store as it creates a key with the role to be able to submit to the store, etc. Setup consists of setting up your Google Developers Service Account: Tip: If you see Google Play Console or Google Developer Console in your local language, add &hl=en at the end of the URL (before any #…) to switch to English.
Open the Google Play Console
Click the Settings menu entry, followed by API access
Click the CREATE SERVICE ACCOUNT button
Follow the Google Developers Console link in the dialog, which opens a new tab window: Click the CREATE SERVICE ACCOUNT button at the top of the Google Developers Console
Provide a Service account name
Click Select a role and choose Service Accounts > Service Account User
Check the Furnish a new private key checkbox
Make sure JSON is selected as the Key type
Click SAVE to close the dialog
Make a note of the file name of the JSON file downloaded to your computer
Back on the Google Play Console, click DONE to close the dialog
Click on Grant Access for the newly added service account
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.)
Click ADD USER to close the dialog
Now that we got our JSON Key, we can run fastlane supply init
and follow the command prompts. Now we can modify our Fastlane
file to have two lanes: Release and Beta; which will tie into the Production track and Beta track in google release management.
default_platform(:android)
What’s Next?
At this point, we have accomplished a method to maintain profiles, certificates, etc. We created two lanes that build the application and uploads to the app store or test flight. However, we’re missing the in-between things that need to happen per build – such as adding increment build numbers and commit the build files to GitHub. In the next parts of this series, we will introduce processes for iOS or Android to implement CI Tools into Fastlane and deploy using AWS to host our pipeline.