How to build React Native Android app with GitHub Actions
In this post, I’m going to automate the build process (see my previous post) for a React Native Android app with GitHub Actions. This obviously ties into the the DevOps tidal wave but in a way that’s very developer friendly. Developers spend significant time in GitHub and have a great developer experience (DX). Instead of popping out to external systems, you can build, test and deploy you app within GitHub and also leverage the extensive, open GitHub Marketplace to reuse workflows and integration into other systems. I also love the fact that all of your workflow is source controlled in the same repo with you code.
Future posts will build on this workflow approach to automated the creation of SBOMs, automatically security and privacy test mobile apps and more.
Note: between the time I originally wrote this blog and then published it, the Joplin repo added a GitHub Action to build the Android app. So I’ve changed the name of the workflow file below. Definitely check out Joplin’s build-android.yml file as well.
1. Code setup
So folks can follow along, I’ll continue using Joplin, an open source note-taking app. You should first fork the Joplin app into your open account (makes a copy) and then clone the repo to your computer (replace ahoog42
with your GitHub username):
$ mkdir -p ~/spfexpert ; cd ~/spfexpert
$ git clone git@github.com:ahoog42/joplin.git
$ cd joplin
2. Configure GitHub Action build workflow
To configure a new GitHub Action, you create a .yml file under .github\workflows
. GitHub has great documentation on the workflow syntax. You can name the yml file whatever you like so let’s just create a file called .github\workflows\build-android.yml
. Key configuration includes:
name
- what to call the actionon
- when to do it (workflow_dispatch
is really handy for testing)jobs
- these are the specific steps your workflow needs
Create build-android.yml
Below is an working workflow yml file for building the Joplin Android app (see previous blog for steps to build Joplin Android locally) which you could easily adapt for your build process:
$ vim .github/workflows/build-android-spfexpert.yml
name: "Build Android app"
on:
workflow_dispatch:
branches: [dev]
# can add push and pull_request here
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Java
uses: actions/setup-java@v2
with:
java-version: "11"
distribution: "adopt"
cache: "gradle"
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Run Yarn Install
run: |
cd ./packages/app-mobile/
npm i -g corepack
yarn install
- name: Build application
run: |
cd ./packages/app-mobile/android
./gradlew assembleDebug
- name: Upload application
uses: actions/upload-artifact@v2
with:
name: app
path: ./packages/app-mobile/android/app/build/outputs/apk/debug/app-debug.apk
retention-days: 3
Commit changes
Next we’ll want to commit these changes to the forked repo (skip this step if you’re using my forked version vs. your own):
$ git add .github/workflows/build-android-spfexpert.yml
$ git commit .github/workflows/build-android-spfexpert.yml -m 'GH Action to build Android app'
Remove unneeded workflows
Next, let’s removed a few GitHub Action workflows that the main Joplin app repo uses so we do’t kick off unneeded workflows:
$ git rm .github/workflows/github-actions-main.yml
$ git rm .github/workflows/close-stale-issues.yml
$ git rm .github/workflows/build-android.yml
$ git commit -a -m 'remove existing Joplin workflows'
Push committed code
And finally let’s push these changes to the repo:
$ git push
3. Run build workflow
Ok, we’re now ready to kick off the build, using the GitHub UI. You can trigger the workflow based on many different types of GitHub events (e.g. pull requests, commits to a branch, etc.) but for a tutorial and initial testing, manually kicking off with a workflow_dispatch
event is perfect.
To run the workflow, navigate to the GitHub repo (for me, it would be at https://github.com/ahoog42/joplin/ however you will just swap in your GitHub username for ahoog42
:)
GitHub Repo UI -> Actions -> Build Android app -> Run Workflow Dropdown -> Run Workflow
4. Accessing the build
GitHub provides a number of ways to access workflow artifacts. In the workflow configuration, we used the actions/upload-artifact@v2
action and stored the apk as an artifact named app
and set a retention of 3 days. If you are accessing artifacts during the same workflow, you can use the actions/download-artifacts
action. After the workflow, you can access artifacts via the GitHub UI or their Artifacts REST API.
Download artifact via GitHub Artifacts REST API
For automation and integrations, you’ll want to access artifacts via the REST API. Here’s a quick step-by-step:
1. GitHub Personal Access Token
You need to generate a GitHub Personal Access Token and I would suggest a fine-grained GitHub access token since this is a security-related blog after all! I chose the following:
- Repository access -> Only select repositories (and then chose my Joplin fork)
- Repository permissions -> Actions (read-only)
The Actions read-only added the Metadata permission for me automatically. Pay attention to the expiration date and securely store the token if you save it locally.
Then you simply export the GitHub personal access token in your shell:
$ export GH_TOKEN=github_pat_11AAPO<snip>
And then you can easily list artifacts in your repo:
curl -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/repos/ahoog42/joplin/actions/artifacts
and you’ll get a response like:
{
"total_count": 5,
"artifacts": [
{
"id": 410082801,
"node_id": "MDg6QXJ0aWZhY3Q0MTAwODI4MDE=",
"name": "app",
"size_in_bytes": 56994698,
"url": "https://api.github.com/repos/ahoog42/joplin/actions/artifacts/410082801",
"archive_download_url": "https://api.github.com/repos/ahoog42/joplin/actions/artifacts/410082801/zip",
"expired": false,
"created_at": "2022-10-24T20:56:13Z",
"updated_at": "2022-10-24T20:56:14Z",
"expires_at": "2022-10-27T20:56:06Z",
"workflow_run": {
"id": 3316021778,
"repository_id": 553849935,
"head_repository_id": 553849935,
"head_branch": "dev",
"head_sha": "8550cdb12d93cee85724f9dd0373a14ec747b680"
}
},
<snip>
You can see there is an array of artifacts (5 in this example, most recent at the top or artifacts[0]
) which you easily then download using the archive_download_url
:
$ curl -LO -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/repos/ahoog42/joplin/actions/artifacts/410082801/zip
When a workflow uploads an artifact, it uses compression (zip) to reduce data storage and group multiple files together. As you may know, apk files are actually zip files so after you download the apk artifact, you’ll actually have a double zipped file (by default, the downloaded file is actually named zip):
$ file zip
which will return:
zip: Zip archive data, at least v2.0 to extract, compression method=deflate
$ unzip -l zip
Archive: zip
Length Date Time Name
--------- ---------- ----- ----
56994698 10-24-2022 20:56 app-release.apk
--------- -------
56994698 1 file
You can verify the file type with the file
command and then list off the file contents with unzip -l zip
. To extract the artifact, you can simply omit the -l
:
$ unzip zip
Archive: zip
inflating: app-debug.apk
And you’ll now have the apk artifact available locally.