Skip to content

Build Android APK

Build Android APK #10

Workflow file for this run

name: Build Android APK
on:
workflow_dispatch:
inputs:
build_type:
description: 'Build type'
required: true
default: 'debug'
type: choice
options:
- debug
- release
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: web/package-lock.json
- name: Install web dependencies
run: npm ci
working-directory: web
- name: Build web app
run: npm run build
working-directory: web
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install Android 34 SDK packages
run: sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
- name: Accept Android SDK licenses
run: yes | sdkmanager --licenses
- name: Add Capacitor Android platform
run: |
if [ ! -d android ]; then
npx cap add android
fi
working-directory: web
- name: Generate Android app icon assets
run: npx @capacitor/assets generate --android --assetPath public --iconBackgroundColor '#0b1020' --iconBackgroundColorDark '#0b1020'
working-directory: web
- name: Sync Capacitor project
run: npx cap sync android
working-directory: web
- name: Read app version
id: version
run: |
APP_VERSION=$(node -p "require('./web/package.json').version")
VERSION_CODE=$(node -e "const [major, minor, patch] = process.argv[1].split('.').map(Number); console.log((major * 10000) + (minor * 100) + patch)" "$APP_VERSION")
echo "app_version=$APP_VERSION" >> "$GITHUB_OUTPUT"
echo "version_code=$VERSION_CODE" >> "$GITHUB_OUTPUT"
- name: Sync Android version metadata
run: node web/scripts/sync-android-version.mjs "${{ steps.version.outputs.app_version }}" "${{ steps.version.outputs.version_code }}"
- name: Build APK
run: ./gradlew assemble${{ inputs.build_type == 'debug' && 'Debug' || 'Release' }} --stacktrace --no-daemon
working-directory: web/android
- name: Require signing secrets for tagged release
if: startsWith(github.ref, 'refs/tags/v')
run: |
test -n "$ANDROID_KEYSTORE_BASE64" || (echo "Missing ANDROID_KEYSTORE_BASE64" && exit 1)
test -n "$ANDROID_KEYSTORE_PASSWORD" || (echo "Missing ANDROID_KEYSTORE_PASSWORD" && exit 1)
test -n "$ANDROID_KEY_ALIAS" || (echo "Missing ANDROID_KEY_ALIAS" && exit 1)
test -n "$ANDROID_KEY_PASSWORD" || (echo "Missing ANDROID_KEY_PASSWORD" && exit 1)
- name: Decode Android release keystore
if: ${{ inputs.build_type == 'release' && env.ANDROID_KEYSTORE_BASE64 != '' && env.ANDROID_KEYSTORE_PASSWORD != '' && env.ANDROID_KEY_ALIAS != '' && env.ANDROID_KEY_PASSWORD != '' }}
run: |
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > web/android/release.keystore
- name: Sign release APK
if: ${{ inputs.build_type == 'release' && env.ANDROID_KEYSTORE_BASE64 != '' && env.ANDROID_KEYSTORE_PASSWORD != '' && env.ANDROID_KEY_ALIAS != '' && env.ANDROID_KEY_PASSWORD != '' }}
run: |
"$ANDROID_HOME/build-tools/34.0.0/apksigner" sign \
--ks web/android/release.keystore \
--ks-pass pass:"$ANDROID_KEYSTORE_PASSWORD" \
--ks-key-alias "$ANDROID_KEY_ALIAS" \
--key-pass pass:"$ANDROID_KEY_PASSWORD" \
--out web/android/app/build/outputs/apk/release/app-release-signed.apk \
web/android/app/build/outputs/apk/release/app-release-unsigned.apk
- name: Verify release APK signature
if: ${{ inputs.build_type == 'release' && env.ANDROID_KEYSTORE_BASE64 != '' && env.ANDROID_KEYSTORE_PASSWORD != '' && env.ANDROID_KEY_ALIAS != '' && env.ANDROID_KEY_PASSWORD != '' }}
run: |
"$ANDROID_HOME/build-tools/34.0.0/apksigner" verify --verbose web/android/app/build/outputs/apk/release/app-release-signed.apk
- name: Select APK path
id: apk
run: |
if [ "${{ inputs.build_type }}" = "debug" ]; then
echo "path=web/android/app/build/outputs/apk/debug/app-debug.apk" >> "$GITHUB_OUTPUT"
elif [ -f web/android/app/build/outputs/apk/release/app-release-signed.apk ]; then
echo "path=web/android/app/build/outputs/apk/release/app-release-signed.apk" >> "$GITHUB_OUTPUT"
else
echo "path=web/android/app/build/outputs/apk/release/app-release-unsigned.apk" >> "$GITHUB_OUTPUT"
fi
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: opencode-remote-${{ inputs.build_type }}-apk-v${{ steps.version.outputs.app_version }}
path: ${{ steps.apk.outputs.path }}
- name: Publish GitHub release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: OpenCode Remote v${{ steps.version.outputs.app_version }}
files: ${{ steps.apk.outputs.path }}
generate_release_notes: true
body: |
## Release
Stable Android release APK for OpenCode Remote `${{ steps.version.outputs.app_version }}`.
## Notes
- Built from `${{ github.sha }}`
- Version code `${{ steps.version.outputs.version_code }}`