Github Actions is the workflow automation tool with CI/CD that allows you to do some tasks, such as running test suite, deploying code and etc based on the Github Events and Types. When an event is triggered, your defined workflow will be run and help you to do some awesome jobs.
Today, I’m going to show you how to create the workflow of building your React Native apps and publishing them to App Store Connect and Play Store using Github Actions and Fastlane. By end of this post, we will be able to build and publish iOS and Android app to respective store when a new Github Release is published. Sounds exciting? Let’s get started!
Creating Workflow
First, we will have to create a workflow in .github/workflows
directory. Similar to other CI/CD services, you may configure the workflow using YAML syntax. Multiple workflow files can be created in the directory and each workflow must have at least a Job.
Now, let’s create a publish.yml
workflow and put a name for the workflow.
name: Publish iOS and Android App to App Store and Play Store
Setting Trigger Event
We want to trigger the workflow when a Github Release is published. Thus, we will be using the release
event in Github Actions to trigger our workflow. Besides release
event, Github Actions also contains many events such pull_request
, push
, issues
that you can hook on and run the workflow. Many events such as release
, pull_request
may have more than one type of activities. For example, the release
event will be triggered when a release is published
, created
, unpublished
, edited
and etc.
In this post, we want to trigger the workflow when the event is release
and the activity type is published
.
name: Publish React Native App to App Store and Play Store
+ on:
+ release:
+ type: [published]
If you want to trigger event with more than one activity type, you may add more activity type into the array. Other than web hook events, workflows can be triggered by scheduled job and external events too.
To understand more about triggering events, please read Events that trigger workflow
Creating Jobs
As mentioned above, each workflow must have at least a Job. Since we are building iOS and Android app, let’s add two jobs: release-ios
and release-android
in the workflow.
name: Publish React Native App to App Store and Play Store
on:
release:
type: [published]
+jobs:
+ release-ios:
+ name: Build and release iOS app
+ runs-on: macOS-latest
+
+ release-android:
+ name: Build and release Android app
+ runs-on: ubuntu-latest
By default, every job will be started simultaneously. If you want to start a job after another job is completed, you can use needs
to specify the dependencies of a job.
jobs:
job1:
job2:
needs: job1
job3:
needs: [job1, job2]
In this example, job2
will starts after job1
is completed and job3
will starts after job1
and job2
is completed.
For every job, we will also need to specify which OS that the job should runs on, Github Actions provides three essential OS which is windows
, ubuntu
and macOS
. In this post, we will use macOS-latest
for iOS build and ubuntu-latest
for Android build
To understand more about the virtual environment, please read Virtual environment for Github Actions
Defining Steps
Now, we can start to add the steps of building and releasing the iOS and Android app. Github Actions provides several standard actions for us to perform certain tasks such as checkout the repository, installing Node, installing Ruby and etc. You can create your own actions if you want to. We will use few standard actions such as actions/checkout
, actions/setup-node
and actions/setup-ruby
to setup the environment.
name: Publish React Native App to App Store and Play Store
on:
release:
type: [published]
jobs:
release-ios:
name: Build and release iOS app
runs-on: macOS-latest
+ steps:
+ - uses: actions/[email protected]
+ - uses: actions/[email protected]
+ with:
+ node-version: '10.x'
+ - uses: actions/[email protected]
+ with:
+ ruby-version: '2.x'
+ - name: Install Fastlane
+ run: bundle install
+ - name: Install packages
+ run: yarn install
release-android:
name: Build and release Android app
runs-on: ubuntu-latest
+ steps:
+ - uses: actions/[email protected]
+ - uses: actions/[email protected]
+ with:
+ node-version: '10.x'
+ - uses: actions/[email protected]
+ with:
+ ruby-version: '2.x'
+ - name: Install Fastlane
+ run: bundle install
+ - name: Install packages
+ run: yarn install
In the workflow above, we have added few steps as following:
actions/[email protected]
– Checkout the current repository.actions/[email protected]
– Install Node 10.x to run React Native >= 0.60actions/[email protected]
– Install Ruby 2.x for the usage of Fastlanebundle install
– Install Fastlaneyarn install
– Install NPM packages
Now, we have all the basic setup in place and we will continue to add more steps to build iOS and Android app using Fastlane. Let’s start with building and publishing Android app.
Build and Publish Android app
There are 2 things that we need to build and publish Android app:
keystore
– Signing the APK. Learn more about how to create your keystore.Google Credentials
– Authenticate with Play Console for publishing the app. Learn more about how to create your Google Credential.
After you have created the keystore and the Google Credential, you may want to encrypt them using command gpg --symmetric --cipher-algo AES256 path/to/your-secret.json
and commit to your repository.
Now, let’s create a script to decrypt the keystore and the Google Credential so that we can use them in our workflow. Create decrypt.sh
and add the following codes:
#!/bin/sh
# --batch to prevent interactive command --yes to assume "yes" for questions
gpg --quiet --batch --yes --decrypt --passphrase="$ENCRYPT_PASSWORD" \
--output ./path/to/release.keystore ./path/to/release.keystore.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$ENCRYPT_PASSWORD" \
--output ./path/to/google-key.json ./path/to/google-key.json.gpg
The ENCRYPT_PASSWORD
is the password that you used to encrypt your secret files and we will put it as a environment variable later. Now let’s add the remaining steps to complete the Android workflow.
name: Publish React Native App to App Store and Play Store
on:
release:
type: [published]
jobs:
release-ios:
...
release-android:
name: Build and release Android app
runs-on: ubuntu-latest
steps:
...
+ - name: Decrypt keystore and Google Credential
+ run: ./path/to/decrypt.sh
+ env:
+ ENCRYPT_PASSWORD: ${{ secrets.ENCRYPT_PASSWORD }}
+ - name: Bundle and Upload to PlayStore
+ run: bundle exec fastlane build_and_release_to_play_store versionName:${{ github.event.release.tag_name }}
+ env:
+ STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
+ KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
We are using the name of the tag
as the versionName
of the app. To add environment variables in Github Actions, we can add env
in the steps that need the variables. If you want to add any secrets, you may add the secrets to your Github repository’s Settings and access them using ${{ secrets.YOUR_SECRET_NAME }}
.
We are almost there. Let’s add the build_and_release_to_play_store
action in the Fastfile
.
lane :build_and_release_to_play_store do |options|
# Bundle the app
gradle(
task: 'bundle',
build_type: 'Release',
project_dir: "android/",
properties: {
"versionName" => options[:versionName],
"versionCode" => options[:versionCode],
}
)
# Upload to Play Store's Internal Testing
upload_to_play_store(
package_name: 'com.example.app',
track: "internal",
json_key: "./path/to/google-key.json",
aab: "./android/app/build/outputs/bundle/release/app.aab"
)
end
Awesome! We have completed the Android steps and you should able to build and publish the app to Play Store.
Building and Publish iOS app
To build an iOS app, we will need to sign the IPA before upload it to App Store Connect and there is no easy way of doing it in CI/CD environment. Luckily, Fastlane provides the sync_code_signing
action for us to handle the code signing easily. If you have not setup code signing before, please follow the codesigning guideline to generate your certificates and provisioning profiles.
Once you done setup the code signing, you can add the steps to the workflow
name: Publish React Native App to App Store and Play Store
on:
release:
type: [published]
jobs:
release-ios:
name: Build and release iOS app
runs-on: macOS-latest
steps:
...
- name: Login Github User
run: echo -e "machine github.com\n login $PERSONAL_ACCESS_TOKEN" >> ~/.netrc
env:
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- name: Build and Upload to TestFlight
run: bundle exec fastlane build_and_release_to_app_store versionName:${{ github.event.release.tag_name }} versionCode:${{ github.run_number }}
env:
FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
release-android:
...
In Github Actions, we can only access the current repository that the workflow is running. If you want to checkout other private repositories, you may create a <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line" target="_blank">personal access token</a>
and login using netrc echo -e "machine github.com\n login $PERSONAL_ACCESS_TOKEN" >> ~/.netrc
. We have added this step to allow us to checkout the code signing repository.
In addition, we will need to add FASTLANE_PASSWORD
and MATCH_PASSWORD
in the environment variables so that Fastlane able to decrypt the certificates and provisioning profiles as well as authenticate with App Store Connect.
If your Apple account has activated two-factor authentication, you may want to add FASTLANE_SESSION
and FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
into the environment variables, otherwise the workflow will fail. Read Fastlane two-factor or two-steps auth to learn more.
Now let’s add build_and_release_to_app_store
actions into Fastfile.
lane :buid_and_release_to_play_store do |options|
...
end
lane :build_and_release_to_app_store do |options|
# Set the build number
increment_build_number(
build_number: options[:versionCode],
xcodeproj: "./ios/Example.xcodeproj"
)
# Set the version name
increment_version_number(
version_number: options[:versionName],
xcodeproj: "./ios/Example.xcodeproj"
)
# Create a custom keychain for code signing
create_keychain(
name: 'keychain',
password: 'password',
default_keychain: true,
unlock: true,
timeout: 3600,
add_to_search_list: true
)
# Import the appstore code signing
match(
type: "appstore",
keychain_name: 'keychain',
keychain_password: 'password',
readonly: true
)
# Building the iOS app
gym(
workspace: "./ios/Example.xcworkspace",
include_bitcode: true,
include_symbols: true,
silent: true,
clean: true,
scheme: "Example",
export_method: "app-store"
)
# Upload to testflight
testflight(
app_identifier: "com.example.app",
username: "[email protected]",
skip_submission: true,
skip_waiting_for_build_processing: true
)
end
Awesome! We have completed the iOS job! Now you should able to build and publish your iOS app to App Store Connect now.
Testing Your Workflow
To test your workflow, you can create a Release and go to the Actions tab in Github to view the log of your workflow.
Github Actions is here for you!
Github Actions is a very awesome tool for every developer to start with CI/CD journey. To understand more about Github Actions, please read their documentation. Have fun trying out Github Actions 😊 !