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!
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
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
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
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/checkout@v1
+ - uses: actions/setup-node@v1
+ with:
+ node-version: '10.x'
+ - uses: actions/setup-ruby@v1
+ 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/checkout@v1
+ - uses: actions/setup-node@v1
+ with:
+ node-version: '10.x'
+ - uses: actions/setup-ruby@v1
+ 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/checkout@v1
- Checkout the current repository.actions/setup-node@v1
- Install Node 10.x to run React Native
>= 0.60
actions/setup-ruby@v1
- Install Ruby 2.x for the usage of
Fastlane
bundle install
- Install Fastlaneyarn install
- Install NPM packagesNow, 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.
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.
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
personal access token
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: "your_apple_username@mail.com",
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.
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 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 😊 !
Copyright © 2024 Tek Min Ewe