Skip to content

Commit dbe1e0a

Browse files
committed
feat: setup GitHub Actions workflow for automated Android releases
- Add GitHub Actions workflow for building and releasing Android APKs on tag push - Configure automated signing with release keystore - Auto-upload to GitHub releases with proper naming convention - Update build.gradle to use proper release signing configuration - Add comprehensive setup documentation
1 parent ff169f8 commit dbe1e0a

File tree

3 files changed

+253
-1
lines changed

3 files changed

+253
-1
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
name: Android Release Build
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
env:
9+
NODE_VERSION: '18'
10+
JAVA_VERSION: '17'
11+
12+
jobs:
13+
build:
14+
name: Build and Release Android APKs
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: ${{ env.NODE_VERSION }}
25+
cache: 'npm'
26+
27+
- name: Setup Java JDK
28+
uses: actions/setup-java@v4
29+
with:
30+
distribution: 'temurin'
31+
java-version: ${{ env.JAVA_VERSION }}
32+
33+
- name: Install dependencies
34+
run: npm ci
35+
36+
- name: Setup Gradle
37+
uses: gradle/gradle-build-action@v2
38+
39+
- name: Make gradlew executable
40+
run: chmod +x android/gradlew
41+
42+
- name: Decode Keystore
43+
env:
44+
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
45+
run: |
46+
echo $KEYSTORE_BASE64 | base64 -d > android/app/release.jks
47+
48+
- name: Build Release APKs
49+
env:
50+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
51+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
52+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
53+
run: |
54+
cd android
55+
./gradlew assembleRelease \
56+
-Pandroid.injected.signing.store.file=release.jks \
57+
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
58+
-Pandroid.injected.signing.key.alias=$KEY_ALIAS \
59+
-Pandroid.injected.signing.key.password=$KEY_PASSWORD
60+
61+
- name: Get tag name
62+
id: tag
63+
run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
64+
65+
- name: Create Release
66+
id: create_release
67+
uses: actions/create-release@v1
68+
env:
69+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70+
with:
71+
tag_name: ${{ steps.tag.outputs.tag }}
72+
release_name: Release ${{ steps.tag.outputs.tag }}
73+
draft: false
74+
prerelease: false
75+
body: |
76+
## What's Changed
77+
- Android release build for version ${{ steps.tag.outputs.tag }}
78+
79+
## APK Downloads
80+
This release includes split APKs for different architectures:
81+
- **arm64-v8a**: For modern 64-bit ARM devices (recommended for most users)
82+
- **armeabi-v7a**: For older 32-bit ARM devices
83+
- **x86_64**: For 64-bit x86 devices (emulators, some tablets)
84+
- **x86**: For 32-bit x86 devices (older emulators)
85+
86+
Download the appropriate APK for your device architecture.
87+
88+
- name: Upload arm64-v8a APK
89+
uses: actions/upload-release-asset@v1
90+
env:
91+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
92+
with:
93+
upload_url: ${{ steps.create_release.outputs.upload_url }}
94+
asset_path: android/app/build/outputs/apk/release/app-arm64-v8a-release.apk
95+
asset_name: alternate-arm64-v8a-${{ steps.tag.outputs.tag }}.apk
96+
asset_content_type: application/vnd.android.package-archive
97+
98+
- name: Upload armeabi-v7a APK
99+
uses: actions/upload-release-asset@v1
100+
env:
101+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
102+
with:
103+
upload_url: ${{ steps.create_release.outputs.upload_url }}
104+
asset_path: android/app/build/outputs/apk/release/app-armeabi-v7a-release.apk
105+
asset_name: alternate-armeabi-v7a-${{ steps.tag.outputs.tag }}.apk
106+
asset_content_type: application/vnd.android.package-archive
107+
108+
- name: Upload x86_64 APK
109+
uses: actions/upload-release-asset@v1
110+
env:
111+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
112+
with:
113+
upload_url: ${{ steps.create_release.outputs.upload_url }}
114+
asset_path: android/app/build/outputs/apk/release/app-x86_64-release.apk
115+
asset_name: alternate-x86_64-${{ steps.tag.outputs.tag }}.apk
116+
asset_content_type: application/vnd.android.package-archive
117+
118+
- name: Upload x86 APK
119+
uses: actions/upload-release-asset@v1
120+
env:
121+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
122+
with:
123+
upload_url: ${{ steps.create_release.outputs.upload_url }}
124+
asset_path: android/app/build/outputs/apk/release/app-x86-release.apk
125+
asset_name: alternate-x86-${{ steps.tag.outputs.tag }}.apk
126+
asset_content_type: application/vnd.android.package-archive
127+
128+
- name: Upload metadata
129+
uses: actions/upload-release-asset@v1
130+
env:
131+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
132+
with:
133+
upload_url: ${{ steps.create_release.outputs.upload_url }}
134+
asset_path: android/app/build/outputs/apk/release/output-metadata.json
135+
asset_name: output-metadata.json
136+
asset_content_type: application/json

ANDROID_RELEASE_SETUP.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# GitHub Actions Android Release Setup
2+
3+
This document explains how to set up the GitHub Actions workflow for automated Android releases.
4+
5+
## Required GitHub Secrets
6+
7+
You need to add the following secrets to your GitHub repository:
8+
9+
### 1. KEYSTORE_BASE64
10+
11+
Your release keystore file encoded in base64.
12+
13+
To generate this:
14+
15+
```bash
16+
# Navigate to your keystore directory
17+
cd keystore
18+
# Encode your keystore file to base64
19+
base64 -i alternate-release-key.jks | tr -d '\n' > keystore_base64.txt
20+
```
21+
22+
Copy the contents of `keystore_base64.txt` and add it as `KEYSTORE_BASE64` secret in GitHub.
23+
24+
### 2. KEYSTORE_PASSWORD
25+
26+
The password for your keystore file.
27+
28+
### 3. KEY_ALIAS
29+
30+
The alias of your signing key within the keystore.
31+
32+
### 4. KEY_PASSWORD
33+
34+
The password for your signing key.
35+
36+
## How to Add Secrets to GitHub
37+
38+
1. Go to your GitHub repository
39+
2. Click on **Settings** tab
40+
3. In the left sidebar, click **Secrets and variables****Actions**
41+
4. Click **New repository secret**
42+
5. Add each secret with the exact names mentioned above
43+
44+
## Workflow Trigger
45+
46+
The workflow is triggered when you push a tag that starts with 'v':
47+
48+
```bash
49+
# Create and push a tag
50+
git tag v1.1.0
51+
git push origin v1.1.0
52+
```
53+
54+
## What the Workflow Does
55+
56+
1. **Setup Environment**: Installs Node.js and Java
57+
2. **Install Dependencies**: Runs `npm ci` to install project dependencies
58+
3. **Build APKs**: Uses Gradle to build release APKs for all architectures
59+
4. **Sign APKs**: Signs the APKs with your release keystore
60+
5. **Create Release**: Creates a GitHub release with the tag
61+
6. **Upload APKs**: Uploads all split APKs to the release
62+
63+
## Generated APK Files
64+
65+
The workflow creates the following APK files:
66+
67+
- `alternate-arm64-v8a-[tag].apk` - For modern 64-bit ARM devices
68+
- `alternate-armeabi-v7a-[tag].apk` - For older 32-bit ARM devices
69+
- `alternate-x86_64-[tag].apk` - For 64-bit x86 devices
70+
- `alternate-x86-[tag].apk` - For 32-bit x86 devices
71+
- `output-metadata.json` - Build metadata
72+
73+
## Troubleshooting
74+
75+
### Common Issues:
76+
77+
1. **Keystore decoding fails**: Make sure your base64 encoding doesn't contain newlines
78+
2. **Signing fails**: Verify your keystore password, key alias, and key password are correct
79+
3. **Build fails**: Check that your dependencies are properly defined in package.json
80+
81+
### Testing the Build Locally
82+
83+
Before pushing a tag, you can test the build process locally:
84+
85+
```bash
86+
# Install dependencies
87+
npm ci
88+
89+
# Prebuild
90+
npx expo prebuild --platform android --clean
91+
92+
# Build (replace with your actual keystore details)
93+
cd android
94+
./gradlew assembleRelease \
95+
-Pandroid.injected.signing.store.file=../keystore/alternate-release-key.jks \
96+
-Pandroid.injected.signing.store.password=YOUR_KEYSTORE_PASSWORD \
97+
-Pandroid.injected.signing.key.alias=YOUR_KEY_ALIAS \
98+
-Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD
99+
```
100+
101+
## File Locations
102+
103+
After a successful build, APKs will be located at:
104+
105+
- `android/app/build/outputs/apk/release/app-arm64-v8a-release.apk`
106+
- `android/app/build/outputs/apk/release/app-armeabi-v7a-release.apk`
107+
- `android/app/build/outputs/apk/release/app-x86_64-release.apk`
108+
- `android/app/build/outputs/apk/release/app-x86-release.apk`

android/app/build.gradle

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ android {
102102
keyAlias 'androiddebugkey'
103103
keyPassword 'android'
104104
}
105+
release {
106+
if (project.hasProperty('android.injected.signing.store.file')) {
107+
storeFile file(project.property('android.injected.signing.store.file'))
108+
storePassword project.property('android.injected.signing.store.password')
109+
keyAlias project.property('android.injected.signing.key.alias')
110+
keyPassword project.property('android.injected.signing.key.password')
111+
}
112+
}
105113
}
106114
buildTypes {
107115
debug {
@@ -110,7 +118,7 @@ android {
110118
release {
111119
// Caution! In production, you need to generate your own keystore file.
112120
// see https://reactnative.dev/docs/signed-apk-android.
113-
signingConfig signingConfigs.debug
121+
signingConfig signingConfigs.release
114122
// shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
115123
// minifyEnabled enableProguardInReleaseBuilds
116124
shrinkResources true

0 commit comments

Comments
 (0)